使用范围重构has_many

时间:2013-07-03 08:31:43

标签: ruby-on-rails scope model-associations

我是新手,我只是向专家展示了我的代码,告诉我我不应该使用has_many过滤我的变量,而是scopes

我有三种模式:用户,产品和所有权。

所以这是我在 app / models / user.rb 中的代码:

class User
  has_many :ownerships, foreign_key: "offerer_id",
                         dependent: :destroy
  has_many :owned_products, through: :ownerships,
                             source: :product
  has_many :future_ownerships, -> { where owning_date: nil, giving_date: nil },
                               class_name: "Ownership",
                               foreign_key: "offerer_id"
  has_many :wanted_products, through: :future_ownerships,
                             source: :product
end

所以我删除了has_many :future_ownershipshas_many :wanted_products,并在 app / models / ownership.rb 中创建了一个范围:

class Ownership
  scope :future, -> { where owning_date: nil, giving_date: nil }
end

现在我可以找到未来的所有权:user.ownerships.future。但我不知道的是,如何找回想要的产品?如何在我的 app / models / product.rb 中创建一个范围,以便能够输入类似的内容:

user.owned_products.wanted

1 个答案:

答案 0 :(得分:2)

您的关联中的条件没有任何本质上的错误,特别是如果您需要急切加载一部分产品。

但是要实现所需的范围,必须将其添加到Product模型上并使用普通的sql,因为过滤器应用于与其定义的模型不同的模型上。

class Product
  # not tested 
  scope :wanted, ->{ where("ownerships.owning_dates IS NULL AND ...") }
end

恕我直言,你最好用第一个解决方案。原因是,如果由于某种原因你将该范围应用于许多用户的块中,尽管急切地加载了产品,你仍然会碰到O(n)墙。

User.includes(:owned_products).each do |user|
  user.onwned_products.wanted # => SQL connection
end

更新:刚刚发现了merge令人惊讶undocumented feature of ActiveRecord

  

在其他用途​​中,它允许您进行连接,并按连接模型上的命名范围进行过滤

换句话说,你可以这样做:

user.owned_products.merge(Ownership.future)

退出强大!