I'm feeling a little bit dumb to ask this, but I've been Googling my a*# off.
Well I have the following models:
class Company < ActiveRecord::Base
has_many :employments
has_many :users, through: :employments
validates_presence_of :name
validates_presence_of :description
validates_numericality_of :zip, only_integer: true
validates_presence_of :email
validates_presence_of :street
validates_presence_of :city
validates_presence_of :country
validates_presence_of :telephone
end
class Employment < ActiveRecord::Base
belongs_to :user
belongs_to :company
end
class User < ActiveRecord::Base
has_many :employments
has_many :companies, through: :employments
end
Important here is the company-Model which has some validations.
Now, I have the following Controller to create a new Company:
class CompaniesController < ApplicationController
def create
#company = Company.new(company_params) # The params were set with a private Method
#employment = #company.employments.build(user: current_user, is_admin: true)
if #employment.save
redirect_to :back, flash: { success: 'Success' }
else
#title = 'Create a new company'
render :new
end
end
end
The Problem is, that when I leave the companies-Fields blank, the company gets not created, but the employment-Model gets persistet in the Database.
I believe It has something to do with the Company.new()-Call I have to check, if the #company-Model gets created first, before the #employment-Model gets created.
How can I achieve that the validation gets tested first?
Thank you very much!
To validate associated object you need to use validates_associated. Please note the "Warning" and "Note" in the linked api document.
Try:
class Employment < ActiveRecord::Base
belongs_to :user
belongs_to :company
validates_associated :company
end
in addition to vinodadhikary's answer, you can also try saving the company. so instead of #employment.save, use #company.save. That should also save #employment when #company passes validations.
Related
I am trying to build a has_many model in the belongs_to model association in rails. The association is correct, but it shows error "must exist". I tried putting the optional: true, but it does not seem to be working.
Models
class User::Product < ApplicationRecord
has_one: :promo_code
end
class User::PromoCode < ApplicationRecord
belongs_to: :product, optional: true
accepts_nested_attributes_for :product
end
PromoCodesController
def new
#promo_code = User::PromoCode.new
#product.build_product
end
def create
#promo_code = User::PromoCode.new(promo_code_params)
#promo_code.save
end
def promo_code_params
params.require(:user_promo_code).permit(:product_id, :product_attributes => [:name])
end
form
form_with(model: promo_code) do |form|
form.fields_for :product do |f|
f.text_field :name
end
end
When the form saves an error appears saying "must exist", which I am assuming is referring to the foreign key in belongs_to.
Any ideas in what I can be doing wrong? I think the code above is the only relevant code that I have regarding this issue.
Looking into the issue linked by #engineersmnky, it looks as if this is a known bug when using accepts_nested_attributes_for.
This can be solved by using the inverse_of option to clarify the bi-directional relationship:
class User::Product < ApplicationRecord
has_one: :promo_code, inverse_of: :product
end
class User::PromoCode < ApplicationRecord
belongs_to: :product, optional: true, inverse_of: :promo_code
accepts_nested_attributes_for :product
end
Try that and see if it resolves your problem.
try this in the models respectively
has_one :promo_code, -> { PromoCode.order(:id) }, class_name: 'PromoCode',inverse_of: :product
belongs_to :product, inverse_of: :promo_code
So I have a has_many through association where between two tables posts and users:
class Post < ApplicationRecord
has_many :assignments
has_many :users, :through => :assignments
end
class User < ApplicationRecord
has_many :assignments
has_many :posts, :through => :assignments
end
class Assignment < ApplicationRecord
belongs_to :request
belongs_to :user
end
Now in my association table (assignment) there are additional attributes for creator:boolean and editor:boolean.
My question is what's the best way to set these secondary attributes from within the controller?
Having looked around I've got a current solution:
posts_controller.rb:
class PostsController < ApplicationController
def create
params.permit!
#post = Post.new(post_params)
if #post.save
Assignment.handle_post(#post.id, params[:creator], params[:editors])
redirect_to posts_path, notice: "The post #{#post.title} has been created."
else
render "new"
end
end
assignment.rb:
class Assignment < ApplicationRecord
belongs_to :request
belongs_to :user
def self.handle_post(post_id, creator, assignment)
Assignment.where(:post_id => post_id).delete_all
Assignment.create!(:post_id => post_id, :user_id => creator, :creator => true, :editor => false)
if editors.present?
editors.each do |e|
Assignment.create!(:post_id => post_id, :user_id => e, :creator => false, :editor => true)
end
end
end
end
So what is essentially happening is I'm getting the user_ids from the form via params (creator returns 1 id, editors returns an array), and AFTER creating the post I'm deleting all columns associated with the post and recreating them off the new attributes.
The issue I have here is I can't run post validations on these associations (e.g. check a creator is present).
My two questions are as follows:
Is this the correct way to handle secondary attributes?
Is there a way to set the association up and then save it all at once so validations can be performed?
This is a more Rails way to do this:
Use nested attributes
post.rb
class Post < ApplicationRecord
# Associations
has_many :assignments, inverse_of: :post
has_many :users, through: :assignments
accepts_nested_attributes_for :assignments
# Your logic
end
assignment.rb
class Assignment < ApplicationRecord
after_create :set_editors
belongs_to :request
belongs_to :user
belongs_to :post, inverse_of: :assignments
# I would create attribute accessors to handle the values passed to the model
attr_accessor :editors
# Your validations go here
validates :user_id, presence: true
# Your logic
private
def set_editors
# you can perform deeper vaidation here for the editors attribute
if editors.present?
editors.each do |e|
Assignment.create!(post_id: post_id, user_id: e, creator: false, editor: true)
end
end
end
end
And finally, add this to your PostsController
params.require(:post).permit(..., assignments_attributes: [...])
This allows you to create Assignments from the create Post action, will run validations on Post and Assignment and run callbacks for you.
I hope this helps!
I have an order form and when an order is created, a new customer is created as well. For this I have the following models:
class Customer < ActiveRecord::Base
has_many :orders
has_many :subscriptions, through: orders
end
class Order < ActiveRecord::Base
belongs_to :customer
has_many :subscriptions
accepts_nested_attributes_for :customer
accepts_nested_attributes_for :subscriptions
end
class Subscription< ActiveRecord::Base
belongs_to :order
belongs_to :customer
end
On my order page I have this form:
= simple_form_for(#order) do |f|
= render 'order_fields', f: f
= f.simple_fields_for :subscriptions do |subscription|
= render 'subscription_fields', subscription: subscription
= f.simple_fields_for :customer do |customer|
= render 'customer_fields', customer: customer
= f.button :submit
In my OrderController I have:
def new
#order = Order.new
#order.build_customer
#order.subscriptions.build
end
def create
#order = Order.new(order_params)
if #order.save
(.... etc ...)
end
private
def order_params
params.require(:order).permit(
:amount,
customer_attributes: [ :id, :email, :password, :password_confirmation],
subscriptions_attributes: [ :id, :product_id, :customer_id])
end
Almost everything goes well:
- User is created
- Order is created and has customer_id = User.id
- Subscription is created and has order_id = Order.id
But somehow it wont associate the subscription to the customer :(
I keep having Subscription.customer_id = nil
Can someone please point me in the right direction? Is there something wrong in the models? or in the controllers? I have no idea anymore where to look.
Your relationships are set up a little different. Instead of creating a customer_id field on Subscription, I'd expect you'd just have a has_one :customer, through: :order.
If you do this you won't have need for a customer_id attribute on your Subscription model anymore. And if you want the id of the customer from the world-view of a subscription you'd call subscription.customer.id.
You may also want to add inverse_of designations for your relationships in your models (always a good practice to minimize reloading of models from the database).
So, in total, I'd recommend:
class Customer < ActiveRecord::Base
has_many :orders, inverse_of: :customer
has_many :subscriptions, through: orders
end
class Order < ActiveRecord::Base
belongs_to :customer, inverse_of: :orders
has_many :subscriptions, inverse_of: :order
accepts_nested_attributes_for :customer
accepts_nested_attributes_for :subscriptions
end
class Subscription< ActiveRecord::Base
belongs_to :order, inverse_of: :subscriptions
has_one :customer, through: :order # THIS IS THE KEY CHANGE
end
Oh, and then you can remove the customer_id from the permitted attributes for subscriptions_attributes.
UPDATE
Given that Subscription#customer_id is meant to be disjointed from the Customer -> Order -> Subscription relationship... Ignore the above (except for perhaps the inverse_of stuff) and... You should be able to do:
class Customer < ActiveRecord::Base
has_many :subscriptions, through: :orders, after_add: :cache_customer_id
private
def cache_customer_id(subscription)
subscription.update_column(:customer_id, self.id)
end
end
Thanks pdobb! I got it working now with adding in the order.controller:
def create
#order = Order.new(order_params)
if #order.save
#order.subscriptions.each { subscription| subscription.update_column(:customer_id, #order.customer.id) }
end
In my users controller i hav defined-
def user_params
params.require(:user).permit(:name,:email,:password,:password_confirmation)
end
In ticket template
Created by <%= #ticket.user.email %>
when I write ticket.user.name , it displays the name but when i write email, its invisible.
class Ticket < ActiveRecord::Base
belongs_to :project
belongs_to :user
end
class Project < ActiveRecord::Base
has_many :tickets, dependent: :destroy
validates :name, presence: true
end
I assume the email didn't get saved? Go to your Command Line, type rails c, then find that user..maybe User.first if it's the first user...tell me if that user has an email attached to it
How do I add the current user to the user_product table when saving a user's product.
I looked at some info online that shows that I can pass in a parameter to the build method, but this doesnt work. The error message is this "Can't mass-assign protected attributes: user_id"
Product controller:
def new
#product = Product.new
#product.user_products.build(:user_id => current_user)
respond_to do |format|
format.html # new.html.erb
format.json { render json: #product }
end
end
My models are: Product, User, User_Product
class Product < ActiveRecord::Base
attr_accessible :name, :issn, :category, :user_products_attributes
validates_presence_of :name, :issn, :category
validates_numericality_of :issn, :message => "has to be a number"
has_many :user_products
has_many :users, :through => :user_products
accepts_nested_attributes_for :user_products
end
class UserProduct < ActiveRecord::Base
attr_accessible :price
validates_presence_of :price
validates_numericality_of :price, :message => "has to be a number"
belongs_to :user
belongs_to :product
end
class user < ActiveRecord::Base
# devise authentication here
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me
has_many :user_products, :dependent => :destroy
has_many :products, :through => :user_products
end
I suggest the following:
Product
has_many :preferences
has_many :users, :through => preferences
has_one :product_photo # if 1 per product
User
has_many :preferences
has_many :products, :through => :preferences
Preference
belongs_to :user
belongs_to :product
ProductPhoto
belongs_to :product
Whatever you do, make sure you pay attention to capitalization and pluralization as rails is pretty picky about that. If you don't get rails conventions right, the code will not work (and worse you will start writing hacks to get around the perceived 'problem'), for instance User_Product should be UserProduct (or Preference in my answer) not User_Product - for the class definition model name (references to it use lower case and underscore though).