【问题标题】:Ruby on Rails has_many through association objects before saveRuby on Rails has_many 在保存之前通过关联对象
【发布时间】:2012-04-01 11:27:56
【问题描述】:

在 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

如果我像这样构建我的对象: purchase = Purchase.new(item_ids: [1, 2, 3]) 并尝试保存它 item_validation 方法尚未填充项目集合,因此即使已设置项目,它也没有机会在其中任何一个上调用 check_something 方法。

是否可以在我的购买模型和关联模型被持久化之前访问项目集合,以便我可以针对它们运行验证?

如果我将 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

它似乎按照我想要的方式工作,但是我很难相信在我购买之前没有办法直接使用 rails 访问项目集合,并且相关记录被保存到数据库中。

【问题讨论】:

    标签: ruby-on-rails ruby has-many-through


    【解决方案1】:

    尝试在 has_many 和 belongs_to 定义中添加参数 inverse_of:。 inverse_of 参数是其他模型上的关系名称,例如:

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

    不要忘记将它也添加到其他类中,例如 PurchaseItem 和 Item

    希望对你有帮助

    【讨论】:

      【解决方案2】:

      删除您自己的item_ids= 方法 - rails 会为您生成一个(请参阅collection_singular_ids=ids)。这可能已经解决了您的问题。

      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
      

      在查看您的代码时,我想到的第二件事:将验证移至 Item 类。所以:

      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
      

      如果Items 之一无效,您的Purchase 记录也将无效。

      【讨论】:

        【解决方案3】:

        您是否有文档表明 purchase = Purchase.new(item_ids: [1, 2, 3]) 符合您的预期?

        在我看来,您只是将非数据库属性“item_ids”设置为数组(即不创建关联)。

        您的购买模型甚至不应该直接设置任何外键列。相反,purchase_items 表中有具有purchase_iditem_id 的条目。要在您的购买和三个项目之间创建链接,您需要在连接表中创建三个条目。

        如果你只是这样做会发生什么?:

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

        【讨论】:

          【解决方案4】:

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

          【讨论】:

            【解决方案5】:

            我假设您无法访问它们,因为在保存记录之前,Purchase 中的 id 不可用。但是正如您提到的,您可以访问一级关联purchase_items,因此您可以提取所有ID并将它们传递给where for Item

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2012-01-14
              • 2012-06-12
              • 2011-01-13
              • 2010-12-28
              • 2016-08-24
              • 2013-11-24
              • 1970-01-01
              相关资源
              最近更新 更多