如何在 Rails3 中对关联关系使用 unscoped?
Posted
技术标签:
【中文标题】如何在 Rails3 中对关联关系使用 unscoped?【英文标题】:How to use unscoped on associated relations in Rails3? 【发布时间】:2011-06-13 02:09:07 【问题描述】:由于信息安全限制,我对产品有一个默认范围。
class Product < ActiveRecord::Base
has_many :photos
default_scope where('visible = 1')
end
但是,在我的关联照片模型中,我还必须找到不应该显示的产品。
class Photo < ActiveRecord::Base
belongs_to :product
end
my_photo.product
在其他情况下,我可以使用 unscoped 来绕过 default_scope,例如在Product.unscoped.find_by_title('abc')
。然而:
使用记录关联时如何移除范围?
my_photo.unscoped.product
没有意义,因为 my_photo 没有名为 unscoped
的方法。 my_photo.product.unscoped
也没有意义,因为 my_photo.product
可能已经为零。
【问题讨论】:
只是评论:我会在你的情况下使用 sti。 哇。您会“投射”对象而不是使用布尔属性? 我绝对相信,如果您需要一个 default_scope,最好将 sti 与专用对象一起使用。 我不得不“哇!”再次。首先,我已经将 STI 用于我的模型,具有 SimpleProduct、ConfigurableProduct 此外(虽然不是这个问题的一部分),我们在所有相关控制器中使用 around_filter 与 Product.scoping FinancialProduct.scoping InsureanceProduct.scoping yield 。这样,我们就不需要 default_scope。 【参考方案1】:新答案
This question 应该可以帮助您弄清楚如何绕过关联的默认 where 子句。
值得重复的是,如果您经常不得不避免使用范围,那么它可能应该是默认值。创建一个 visible
非默认范围并在您的关联中明确使用它。
【讨论】:
对不起。我很着急,完全搞砸了我的例子。它仅用于说明用例,与我的实际实现几乎无关。请再次阅读我的问题。 好的,这样更好。我怀疑这不是问题;my_photo.product
不应调用 default_scope。【参考方案2】:
哦。我欺骗了自己。认为以下方法行不通...但确实可以:
Product.unscoped do
my_photo.product
end
请注意,您必须使用应该绕过的default_scope
在模型上调用 unscoped。
此外,必须尊重继承。如果您在Product
中有class InsuranceProduct < Product
和class FinancialProduct < Product
和default_scope
,则以下两种组合都可以使用:
InsuranceProduct.unscoped do
my_record.insurance_products
end
FinancialProduct.unscoped do
my_record.financial_products
end
Product.unscoped do
my_record.products
end
但是,尽管Product
中定义了范围,但以下将不起作用:
Product.unscoped do
my_record.financial_products
end
我猜这是 Ruby / Rails 中 STI 的另一个怪癖。
【讨论】:
这个解决方案有一个陷阱:查询必须在块内执行,因此必须确保在has_many
关系上调用#all
或#to_a
。详情见这里:github.com/rails/rails/issues/5591【参考方案3】:
这不是主题,而是关于 ActiveRecord#becomes 的问题:我们(希望)用初始化程序修复了它
类 ActiveRecord::Base def become_with_association_cache(klass) 成为 = become_without_association_cache(klass) 成为.instance_variable_set("@association_cache", @association_cache) 变成了 结尾 alias_method_chain : 成为, :association_cache 结尾https://gist.github.com/2478161
【讨论】:
【参考方案4】:另一种选择是覆盖 getter 方法和 unscope super:
class Photo < ActiveRecord::Base
belongs_to :product
def product
Product.unscoped super
end
end
我遇到了同样的情况,我有一个需要取消作用域的关联模型,但在几乎所有其他情况下,它都需要默认作用域。如果您在多个地方使用关联 getter,这应该可以节省对 unscoped 的额外调用。
【讨论】:
谢谢!不过感觉很hacky...希望有一种方法可以编写belongs_to :product, :unscoped => true
,这样我们就不必在每个模型中重写该方法。
Rails 3 中有这样的东西,在 Rails 4 中坏了:github.com/rails/rails/issues/10643
太棒了!如果照片是默认范围的,有没有办法打电话给product.photos.unscoped
?
Rails 4 正在等待修复:github.com/rails/rails/commit/…
还要注意,即使使用 Photo.includes(:product),这也会产生额外的数据库查询。【参考方案5】:
我可能有点迟到了,但前段时间我发现自己处于同样的情况,我写了一个 gem 来轻松地做到这一点:unscoped_associations。
用法:
belongs_to :user, unscoped: true
支持:
belongs_to 有一个 has_many还支持多态关联。
【讨论】:
感谢@markets,您的 gem 为我节省了一个无法排除 default_scope 的奇怪情况。 我很惊讶这需要宝石!这应该是 rails 的默认功能! 在我使用 .includes 拉入关联的特定情况下对我很有帮助。 我认为最优雅的解决方案!【参考方案6】:在 Rails 4 中,您可以将关联与不受欢迎的过滤器的显式 unscope 一起使用,即my_photo.product.unscope(where: :visible)
【讨论】:
【参考方案7】:如果您需要始终取消特定关联的范围,您可以在定义关联时取消范围:
belongs_to :product, -> unscope(where: :visible)
由于某种原因,特定的 where
键没有为我正确加载,所以我只是取消了整个 where
的范围,这在我的情况下恰好是另一个选项:
belongs_to :product, -> unscope(:where)
其他答案也值得一看,但这是 Rails 4.1+ 的另一种选择。
【讨论】:
在使用 belongs_to :product, -> unscope(:where) 时要非常小心,它会删除整个范围,所以如果你有 something.product 它只会返回数据库中的第一个产品。杀死 where id =...以上是关于如何在 Rails3 中对关联关系使用 unscoped?的主要内容,如果未能解决你的问题,请参考以下文章