如何在 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 &lt; Productclass FinancialProduct &lt; Productdefault_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 =&gt; 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?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Sequelize 中对 belongsToMany 关联进行***查询?

Rails 3 - 关联 - 它是如何工作的?

如何在 Laravel 中对所有行使用多态关系?

在Python中对关联数组进行排序[重复]

您可以在 Rails 3 搜索中对日期进行比较吗?

Rails 3 - 多个关联