重写 Rails default _ scope

如果我有一个 ActiveRecord: : Base 模型,它有一个 default-scope:

class Foo < ActiveRecord::Base


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


end

有没有办法做一个 Foo.find 没有使用的 default_scope条件?换句话说,您可以覆盖默认范围吗?

我本来以为在名称中使用‘ default’会表明它是 曾经是可重写的,否则它就会被称为类似 global_scope的东西,对吗?

98887 次浏览

简短的回答: 不要使用 default_scope,除非你真的必须这样做。使用命名作用域可能会更好。如果需要,可以使用 with_exclusive_scope覆盖默认作用域。

更多细节请看 这个问题

您可以使用 with_exclusive_scope方法重写默认范围。因此:

foos = Foo.with_exclusive_scope { :conditions => ["baz = ?", baz] }

在 Rails 3:

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

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 _ scope,并在任何地方显式地对其进行编码。

陷阱铁轨3!

如果您只需要更改 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

因为 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。这是为了你好。

使用 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)

好吧,您总是可以使用旧时最喜欢的 find_by_sql与完整的查询。 例如: Find _ by _ sql (“ SELECT * FROM model WHERE id = 123”)

在 Rails 5.1 + 上(可能更早,但我已经在5.1上测试过了) ,可以取消某个特定列的作用域,imho 是以一种可以在命名作用域内使用的方式删除 default_scope的理想解决方案。在作战计划 default_scope中,

Foo.unscope(where: :bar)

或者

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

都将导致一个 sql 查询,该查询不应用原始作用域,但应用合并到 arel 中的任何其他条件。