【问题标题】:Overriding a Rails default_scope覆盖 Rails default_scope
【发布时间】:2010-12-22 11:50:19
【问题描述】:

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

class Foo < ActiveRecord::Base

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

end

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

我原以为在名称中使用“默认”会表明它可覆盖的,否则它会被称为 global_scope,对吧?

【问题讨论】:

标签: ruby-on-rails


【解决方案1】:

在 Rails 3 中:

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

【讨论】:

  • 这有一个副作用,如果 Post has_many Comment,Post.first.cmets.unscoped 返回 ALL cmets。
  • 这真的把我搞砸了一段时间。特别是如果你最终把它放在一个类方法中,比如:def self.random; unscoped.order('rand()'); end unscoped 会删除它之前的所有 sql,而不仅仅是 default_scope 下列出的内容。虽然从技术上讲是正确答案,但请小心使用unstopped
  • 警告! Unscoped 不会仅删除 default_scope,它已经在另一条评论中说过,但它确实会搞砸。
  • 一个好的经验法则是只有unscoped可以直接跟随模型,例如Foo.unscoped.blah() 可以,但绝对不行Foo.blah().unscoped
  • stackoverflow.com/questions/1834159/… 解决了 Enrico 提到的副作用
【解决方案2】:

简答:除非你真的必须,否则不要使用default_scope。使用命名范围可能会更好。话虽如此,如果需要,您可以使用with_exclusive_scope 覆盖默认范围。

查看this question了解更多详情。

【讨论】:

  • > 除非你真的需要,否则不要使用 default_scope。一个很好的建议!谢谢!
  • 确实如此。使用 default_scope 似乎是一个好主意,但可能会在您的应用程序的生命周期内引起多个麻烦。
  • 您有点夸大其词了。 default_scope 是一个出色的工具,在某些情况下您可以采用其他方式,但 default_scope 是正确的做法。例如,当您有一个带有inactive 标志的Product 模型时,最好设置default_scope { where inactive: false },因为在99% 或以上的情况下,您都不想显示非活动产品。然后你只需在剩下的 1% 的情况下调用unscoped,这可能是一个管理面板。
  • 默认范围违反了最小惊讶原则。我正忙着诅咒以前的开发人员使用它!
【解决方案3】:

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

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

【讨论】:

  • 提示:定义一个像scope :without_default_order, -&gt; { reorder("") } 这样的范围,你可以做像Foo.without_default_order.order("created_at ASC") 这样的事情。在某些情况下它读起来更好(也许不是这个确切的情况,但我有一个)。
  • Reorder 帮我做了。非常感谢!
【解决方案4】:

由于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

currently 可能是 unscope 之类的::where, :select, :group, :order, :lock, :limit, :offset, :joins, :includes, :from, :readonly, :having

但仍然请尽量避免使用default_scope。这是为了你好。

【讨论】:

  • 这个答案应该更高
【解决方案5】:

您可以使用with_exclusive_scope 方法覆盖默认范围。所以:

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

【讨论】:

【解决方案6】:

在 Rails 5.1+(可能更早,但我已经在 5.1 上测试过)可以取消特定列的范围,恕我直言,这是删除default_scope 的理想解决方案,可以使用在命名范围内。对于 OPdefault_scope

Foo.unscope(where: :bar)

或者

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

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

【讨论】:

    【解决方案7】:

    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!

    【讨论】:

    • 你必须使用Bar.foos.reorder(:created_at =&gt; :asc)
    【解决方案8】:

    在 Rails 3+ 中,您可以结合使用 unscoped 和 merge:

    # 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)
    

    【讨论】:

    • 这也对我有用,首先调用 unscoped (Rails 4.2):User.unscoped.where(email: "foo@example.com")
    【解决方案9】:

    好吧,您始终可以使用旧时最喜欢的find_by_sql 来完成查询。 例如: Model.find_by_sql("SELECT * FROM models WHERE id=123")

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多