简体   繁体   English

Ruby on Rails - 嵌套属性:如何从子模型访问父模型

[英]Ruby on Rails - nested attributes: How do I access the parent model from child model

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. 上面的验证显然不起作用,因为当我从bill表单中的嵌套属性中读取bill_items时,属性bill_item.bill_id或bill_item.bill在保存之前不可用。

So how do I go about doing something like that? 那我该怎么做呢?

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

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 : 在Rails 4中(未在早期版本上测试),您可以通过在has_manyhas_one上设置inverse_of选项来访问父模型:

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 文档: 双向关联

bill_item.bill应该可用,您可以尝试提升self.bill.inspect以查看它是否存在,但我认为问题出在其他地方。

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. set_nest方法完成了这个伎俩。 Wish it came standard with accepts_nested_attributes_for. 希望它与accepts_nested_attributes_for一起标准化。

Yeah, this kind of problem can be annoying. 是的,这种问题可能很烦人。 You could try adding a virtual attribute to your Bill Item model like this: 您可以尝试将虚拟属性添加到Bill Item模型,如下所示:

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. 你可能不会觉得在html中使用store_id是可取的,但它可能不是一个问题。 Let me know if this helps. 如果这有帮助,请告诉我。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM