Rails default_scopeのオーバーライド


155

デフォルトのスコープを持つActiveRecord :: Baseモデルがある場合:

class Foo < ActiveRecord::Base

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

end

条件Foo.find 使用せずに行う方法はありdefault_scopeますか?つまり、デフォルトのスコープをオーバーライドできますか?

名前に「デフォルト」を使用すると、オーバーライド可能であることを示唆していると思いました、そうでない場合はのような名前になりglobal_scopeますよね?


回答:


151

短い答え:default_scope本当に必要がない限り使用しないでください。名前付きスコープの方が良いでしょう。そうは言っても、with_exclusive_scope必要に応じてを使用してデフォルトのスコープをオーバーライドできます。

詳細については、この質問をご覧ください。


前の質問へのリンクをありがとう
Gareth、

10
>本当に必要がない限り、default_scopeを使用しないでください。素晴らしいアドバイス!ありがとうございました!
installero

2
仰るとおり。使用default_scopeは良い考えのように思えるかもしれませんが、アプリの存続期間中に複数の頭痛の種を引き起こす可能性があります。
thomax


1
あなたは少し大げさです。default_scopeは優れたツールであり、別の方法を使用できる状況もありますが、default_scopeそれはまさに正しいことです。たとえばProductinactiveフラグを持つモデルがあるdefault_scope { where inactive: false }場合、99%またはケースでは非アクティブな製品を表示したくないため、aを設定するのが最善の方法です。次にunscoped、残りの1%のケースを呼び出します。これはおそらく管理パネルです。
ペドロザット2017年

215

Rails 3の場合:

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

58
これには副作用があり、Post has_many Commentの場合、Post.first.comments.unscopedはすべてのコメントを返します。
Enrico Carlesso

3
これは本当に私をしばらく台無しにしました。特に、これを次のようなクラスメソッドに配置する場合:def self.random; unscoped.order('rand()'); endunscopedは、default_scopeにリストされているものだけでなく、その前のすべてのSQLを削除します。技術的には正しい答えですが、使用には注意してくださいunstopped
Schneems '25

7
警告!Unscopedはdefault_scopeのみを削除するのではなく、すでに別のコメントで述べられていますが、実際に混乱する可能性があります。
dsimard 2012

15
親指の良いルールは、するだけでunscoped、それは直接モデルに従うことができたときに、例えばFoo.unscoped.blah()OK決してですFoo.blah().unscoped
Grant Birchmeier、2013

stackoverflow.com/questions/1834159/…Enricoによって言及された副作用を回避します
wbharding

107

で定義した順序を変更するだけの場合はdefault_scopereorderメソッドを使用できます。

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")
Henrik N

注文し直してくれました。どうもありがとう!
Andre Zimpel

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可能であればの使用は避けてください。それはあなた自身のためです。


この答えはもっと高いはずです
スティーブン・コーウィン2017年


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 2013

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

On Rails 5.1+(および以前の可能性がありますが、5.1で動作することをテストしました)特定の列のスコープを解除できます。これは、default_scope名前付きスコープ内で使用できる方法でを削除するための理想的なソリューションです。OPの場合default_scope

Foo.unscope(where: :bar)

または

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

両方とも、元のスコープを適用しないSQLクエリになりますが、他の条件がarelにマージされる場合は適用されます。


2

ええと、いつでもfind_by_sql完全なクエリで昔のお気に入りを使用できます。例:Model.find_by_sql( "SELECT * FROM models WHERE id = 123")

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.