使用范围重构 has_many [英] Refactoring has_many with scopes

查看:42
本文介绍了使用范围重构 has_many的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm a newbie and I just showed my code to an expert, that told me I shouldn't use has_many to filter my variables, but scopes.

我有三个模型:用户、产品和所有权.

I have three models : User, Product and Ownership.

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

So here is my code in 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 中创建了一个范围:

So I deleted the has_many :future_ownerships and has_many :wanted_products, and created a scope in app/models/ownership.rb :

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

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

Now I can find the future ownerships doing this : user.ownerships.future. But what I don't know, is how to retrieve the wanted products ? How can I make a scope in my app/models/product.rb to be able to type something like that :

user.owned_products.wanted

推荐答案

关联中的条件本身并没有什么不好的,特别是如果您需要预先加载产品的子集.

There's nothing inherently bad with conditions in your associations, specially if you need to eager load a subset of products.

然而,要实现您需要的范围,您必须将其添加到 Product 模型中,并使用普通 sql,因为过滤器应用于与它定义的模型不同的模型.

However to achieve the scope you need, you must add it on the Product model and resort to plain sql since the filter is applied on a different model than the one it's defined on.

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

恕我直言,您最好使用第一个解决方案.原因是,如果出于某种原因,您将该范围应用到许多用户的块中,尽管急于加载产品,您仍会遇到 O(n) 墙.

IMHO you're better off with the first solution. The reason is, if for some reason you apply that scope inside a block of many users, you'll hit the O(n) wall despite eager loading the products.

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

更新:刚刚发现merge 一个惊人的未记录的 ActiveRecord 功能.

Update : just found out about merge an amazingly undocumented feature of ActiveRecord.

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

Among other uses, it allows you to do a join, and filter by a named scope on the joined model

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

In other words you can do :

user.owned_products.merge(Ownership.future)

放弃强大!

这篇关于使用范围重构 has_many的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆