问题
I have a couple of models like so
class Bill < ActiveRecord::Base
has_many :bill_items
belongs_to :store
accepts_nested_attributes_for :bill_items
end
class BillItem <ActiveRecord::Base
belongs_to :product
belongs_to :bill
validate :has_enough_stock
def has_enough_stock
stock_available = Inventory.product_is(self.product).store_is(self.bill.store).one.quantity
errors.add(:quantity, "only #{stock_available} is available") if stock_available < self.quantity
end
end
The above validation so obviously doesn't work because when I'm reading the bill_items from nested attributes inside the bill form, the attributes bill_item.bill_id or bill_item.bill are not available before being saved.
So how do I go about doing something like that?
回答1:
This is how I solved it eventually; by setting parent on callback
has_many :bill_items, :before_add => :set_nest
private
def set_nest(bill_item)
bill_item.bill ||= self
end
回答2:
In Rails 4 (didn't test on earlier versions) you can access the parent model by setting the inverse_of
option on has_many
or has_one
:
class Bill < ActiveRecord::Base
has_many :bill_items, inverse_of: :bill
belongs_to :store
accepts_nested_attributes_for :bill_items
end
Documentation: Bi-directional associations
回答3:
The bill_item.bill should be available , you could try to do a raise self.bill.inspect to see if it's there or not, but i think the problem is elsewhere.
回答4:
I "fixed" it by setting parent in a callback:
class Bill < ActiveRecord::Base
has_many :bill_items, :dependent => :destroy, :before_add => :set_nest
belongs_to :store
accepts_nested_attributes_for :bill_items
def set_nest(item)
item.bill ||= self
end
end
class BillItem <ActiveRecord::Base
belongs_to :product
belongs_to :bill
validate :has_enough_stock
def has_enough_stock
stock_available = Inventory.product_is(self.product).store_is(self.bill.store).one.quantity
errors.add(:quantity, "only #{stock_available} is available") if stock_available < self.quantity
end
end
The set_nest method did the trick. Wish it came standard with accepts_nested_attributes_for.
回答5:
Yeah, this kind of problem can be annoying. You could try adding a virtual attribute to your Bill Item model like this:
class BillItem <ActiveRecord::Base
belongs_to :product
belongs_to :bill
attr_accessible :store_id
validate :has_enough_stock
def has_enough_stock
stock_available = Inventory.product_is(self.product).store_is(load_bill_store).one.quantity
errors.add(:quantity, "only #{stock_available} is available") if stock_available < self.quantity
end
private
def load_bill_store
Store.find_by_id(self.store_id)
end
end
And then in your view you could add a hidden field like this:
<%= bill_item.hidden_field :store_id, :value => store_id %>
This hasn't been test but it might work. You might not find it desirable to have the store_id in the html but it may not be a concern. Let me know if this helps.
来源:https://stackoverflow.com/questions/2611459/ruby-on-rails-nested-attributes-how-do-i-access-the-parent-model-from-child-m