简体   繁体   English

Ruby on Rails在保存之前通过关联对象具有很多功能

[英]Ruby on Rails has_many through association objects before save

on a Ruby on Rails project I'm trying to access association objects on an ActiveRecord prior to saving everything to the database. 在Ruby on Rails项目上,我试图在将所有内容保存到数据库之前访问ActiveRecord上的关联对象。

class Purchase < ActiveRecord::Base

  has_many :purchase_items, dependent: :destroy
  has_many :items, through: :purchase_items

  validate :item_validation

  def item_ids=(ids)
    ids.each do |item_id|
      purchase_items.build(item_id: item_id)
    end
  end

  private

  def item_validation
    items.each do |item|
      ## Lookup something with the item
      if item.check_something
        errors.add :base, "Error message"
      end
    end
  end

end

If I build out my object like so: purchase = Purchase.new(item_ids: [1, 2, 3]) and try to save it the item_validation method doesn't have the items collection populated yet, so even though items have been set set it doesn't get a chance to call the check_something method on any of them. 如果我像这样构建我的对象: purchase = Purchase.new(item_ids: [1, 2, 3])并尝试保存它, item_validation方法还没有填充items集合,所以即使已经设置了项目设置它没有机会在任何一个上调用check_something方法。

Is it possible to access the items collection before my purchase model and association models are persisted so that I can run validations against them? 是否可以在我的购买模型和关联模型持久化之前访问项目集合,以便我可以对它们运行验证?

If I change my item_validation method to be: 如果我将item_validation方法更改为:

def item_validation
  purchase_items.each do |purchase_item|
    item = purchase_item.item
    ## Lookup something with the item
    if item.something
       errors.add :base, "Error message"
    end
  end
end

it seems to work the way I want it to, however I find it hard to believe that there is no way to directly access the items collection with rails prior to my purchase and associated records being saved to the database. 它似乎按照我想要的方式工作,但是我发现很难相信在我购买之前没有办法直接访问带有rails的items集合,并且相关记录被保存到数据库中。

Try to adding the argument inverse_of: in the has_many and belongs_to definitions. 尝试在has_many和belongs_to定义中添加参数inverse_of :. The inverse_of argument it's the name of the relation on the other model, For example: inverse_of参数它是另一个模型上关系的名称,例如:

class Post < ActiveRecord::Base
  has_many :comments, inverse_of: :post
end

class Comment < ActiveRecord::Base
  belongs_to :post, inverse_of: :comments
end

Don't forget to add it also on the other classes, such as PurchaseItem and Item 不要忘记将其添加到其他类,例如PurchaseItem和Item

Hope it helps 希望能帮助到你

Remove your own item_ids= method - rails generates one for you (see collection_singular_ids=ids ). 删除你自己的item_ids=方法 - rails为你生成一个(参见collection_singular_ids = ids )。 This might already solve your problem. 这可能已经解决了您的问题。

class Purchase < ActiveRecord::Base

  has_many :purchase_items, dependent: :destroy
  has_many :items, through: :purchase_items

  validate :item_validation

  private

  def item_validation
    items.each do |item|
      ## Lookup something with the item
      if item.check_something
        errors.add :base, "Error message"
      end
    end
  end

end

The second thing that comes in my mind looking at your code: Move the validation to the Item class. 我想到的第二件事就是查看你的代码:将验证移到Item类。 So: 所以:

class Purchase < ActiveRecord::Base
  has_many :purchase_items, dependent: :destroy
  has_many :items, through: :purchase_items
end

class Item < ActiveRecord::Base
  has_many :purchase_items
  has_many :purchases, through: :purchase_items

  validate :item_validation

  private

  def item_validation
      if check_something
        errors.add :base, "Error message"
      end
  end
end

Your Purchase record will also be invalid if one of the Item s is invalid. 如果其中一个Item无效,您的Purchase记录也将无效。

Do you have documentation that indicates purchase = Purchase.new(item_ids: [1, 2, 3]) does what you're expecting? 你有文件表明purchase = Purchase.new(item_ids: [1, 2, 3])做你期望的吗?

To me that looks like you are just setting the non-database attribute 'item_ids' to an array (ie not creating an association). 对我来说,看起来你只是将非数据库属性'item_ids'设置为一个数组(即不创建关联)。

Your Purchase model should not even have any foreign key columns to set directly. 您的购买模型甚至不应该直接设置任何外键列。 Instead there are entries in the purchase_items table that have a purchase_id and item_id . 相反, purchase_items表中的条目具有purchase_iditem_id To create a link between your purchase and the three items you need to create three entries in the joiner table. 要在购买和三个项目之间创建链接,您需要在joiner表中创建三个条目。

What happens if you just do this instead?: 如果你这样做会发生什么?:

purchase = Purchase.new
purchase.items = Item.find([1,2,3]) 

您可以使用model.associations = [association_objects]Association Callback http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Association+callbacks

I assume you can't access them because id of Purchase isn't available before record saved. 我假设您无法访问它们,因为在保存记录之前,“ Purchase id不可用。 But as you mention you have access to first level association purchase_items , so you can extract all ids and pass them in where for Item : 但正如你提到你有机会获得第一级协会purchase_items ,这样你就可以提取所有的ID,并通过他们在whereItem

items = Item.where(purchase_item_id: purchase_items.map(&:id))

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

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