覆盖Rails default_scope


155

如果我有一个带有默认范围的ActiveRecord :: Base模型:

class Foo < ActiveRecord::Base

  default_scope :conditions => ["bar = ?",bar]

end

有什么方法可以Foo.find 使用default_scope条件?换句话说,您可以覆盖默认范围吗?

我以为在名称中使用'default'会暗示它可重写的,否则它将被称为global_scope,对吗?


Answers:


151

简短答案:default_scope除非确实需要,否则请勿使用。使用命名范围可能会更好。话虽如此,您可以根据with_exclusive_scope需要覆盖默认范围。

请查看此问题以获取更多详细信息。


感谢您链接到上一个问题
Gareth,2009年

10
>除非确实需要,否则不要使用default_scope。一个很好的建议!谢谢!
installero

2
如此真实。使用default_scope似乎是一个好主意,但可能会在应用程序的生命周期内引起多次头痛。
thomax


1
您有点夸张先生。default_scope是一种出色的工具,在某些情况下您可以选择其他方式,但这default_scope只是正确的做法。例如,当您的Product模型带有inactive标志时,default_scope { where inactive: false }最好设置a ,因为99%的情况下,您都不想显示无效的产品。然后,您只需调用unscoped其余1%的案例,这可能是管理面板。
pedrozath

215

在Rails 3中:

foos = Foo.unscoped.where(:baz => baz)

58
如果Post has_many Comment,Post.first.comments.unscoped返回所有注释,则会产生副作用。
Enrico Carlesso 2011年

3
这真的让我有些困惑。特别是如果最终将其放在类似以下的类方法中:def self.random; unscoped.order('rand()'); endunscoped会删除其前面的所有sql,而不仅仅是default_scope下列出的内容。从技术上来说,这是正确的答案,但请小心使用unstopped
Schneems 2011年

7
警告!Unscoped不会仅删除default_scope,它已经在另一条评论中说过,但实际上可能使事情变得混乱。
dsimard

15
一个好的经验法则是,仅unscoped当它可以直接遵循模型时,例如Foo.unscoped.blah()可以,但从不Foo.blah().unscoped
Grant Birchmeier


107

如果只需要更改中定义的顺序default_scope,则可以使用reorder方法

class Foo < ActiveRecord::Base
  default_scope order('created_at desc')
end

Foo.reorder('created_at asc')

运行以下SQL:

SELECT * FROM "foos" ORDER BY created_at asc

3
提示:定义一个范围scope :without_default_order, -> { reorder("") },您可以做类似的事情。Foo.without_default_order.order("created_at ASC")在某些情况下,它读起来更好(也许不是这种确切的情况,但是我有一个)。
亨里克·N

重新排序为我做到了。非常感谢!
安德烈·辛珀尔

49

由于4.1可以ActiveRecord::QueryMethods#unscope用来对抗默认范围:

class User < ActiveRecord::Base
  default_scope { where tester: false }
  scope :testers, -> { unscope(:where).where tester: true }
  scope :with_testers, -> { unscope(:where).where tester: [true, false] }
  # ...
end

这是目前能够unscope喜欢的东西::where, :select, :group, :order, :lock, :limit, :offset, :joins, :includes, :from, :readonly, :having

不过还是请尽量避免使用的default_scope,如果你能。是为了您自己的利益。


这个答案应该更高
Stephen Corwin


5

Rails 3 default_scope似乎没有像Rails 2中那样被覆盖。

例如

class Foo < ActiveRecord::Base
  belongs_to :bar
  default_scope :order=>"created_at desc"
end

class Bar < ActiveRecord::Base
  has_many :foos
end

> Bar.foos
  SELECT * from Foo where bar_id = 2 order by "created_at desc";
> Bar.unscoped.foos
  SELECT * from Foo;  (WRONG!  removes the "has" relationship)
> Bar.foos( :order=>"created_at asc" )  # trying to override ordering
  SELECT * from Foo where bar_id = 2 order by "created_at desc, created_at asc"

在我的应用程序中,使用PostgreSQL,默认作用域中的排序为WINS。我要删除所有default_scopes并将其明确地编码在任何地方。

陷阱Rails3!


1
您必须使用Bar.foos.reorder(:created_at => :asc)
Ivan Stana

5

使用Rails 3+,您可以结合使用无作用域和合并:

# model User has a default scope
query = User.where(email: "foo@example.com")

# get rid of default scope and then merge the conditions
query = query.unscoped.merge(query)

这对我也很有效,首先打电话给无范围的人(Rails 4.2):User.unscoped.where(email: "foo@example.com")
Nick

3

在Rails 5.1+(可能是更早的版本,但我已经测试过,它可以在5.1上运行)上,可以取消对特定列的作用域,该imho是以default_scope可在命名范围内使用的方式删除a的理想解决方案。对于OP default_scope

Foo.unscope(where: :bar)

要么

scope :not_default, -> { unscope(where: :bar) }
Foo.not_default

两者都将导致一个不应用原始范围的sql查询,但是会应用任何其他条件合并到区域中。


2

好吧,您始终可以将旧时的收藏夹find_by_sql与完整的查询一起使用。例如:Model.find_by_sql(“ SELECT * FROM models WHERE id = 123”)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.