Überschreiben eines Rails default_scope

155

Wenn ich ein ActiveRecord :: Base-Modell mit einem Standardbereich habe:

class Foo < ActiveRecord::Base

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

end

Gibt es eine Möglichkeit, Foo.find ohne die default_scopeBedingungen zu nutzen? Mit anderen Worten, können Sie einen Standardbereich überschreiben?

Ich hätte gedacht , dass die Verwendung von ‚default‘ im Namen würde vorschlagen , dass es war außer Kraft gesetzt werden , sonst wäre es so etwas wie genannt wird global_scope, nicht wahr?

Gareth
quelle

Antworten:

151

Kurze Antwort: Nicht verwenden, es default_scopesei denn, Sie müssen wirklich. Mit benannten Bereichen sind Sie wahrscheinlich besser dran. with_exclusive_scopeWenn dies erforderlich ist , können Sie den Standardbereich bei Bedarf überschreiben.

Schauen Sie sich diese Frage für weitere Details an.

Pär Wieslander
quelle
Vielen Dank für den Link zur vorherigen Frage
Gareth
10
> Verwenden Sie default_scope nur, wenn Sie es wirklich müssen. Ein ausgezeichneter Rat! Danke dir!
Installero
2
So wahr. Die Verwendung default_scopescheint eine gute Idee zu sein, verursacht jedoch wahrscheinlich mehrere Kopfschmerzen während der Lebensdauer Ihrer App.
Thomax
1
Sie übertreiben ein wenig, Sir. default_scopeist ein ausgezeichnetes Werkzeug und es gibt Situationen, in denen Sie einen anderen Weg gehen könnten, aber es ist default_scopegenau das Richtige. Wenn Sie beispielsweise ein ProductModell mit einer inactiveFlagge haben, default_scope { where inactive: false }ist es am besten, eine zu setzen, da Sie in 99% oder Fällen kein inaktives Produkt anzeigen möchten. Dann rufen Sie einfach unscopeddie verbleibenden 1% -Fälle auf, bei denen es sich wahrscheinlich um ein Admin-Panel handelt.
Pedrozath
215

In Schienen 3:

foos = Foo.unscoped.where(:baz => baz)
Vincent
quelle
58
Dies hat einen Nebeneffekt, wenn Post has_many Comment, Post.first.comments.unscoped ALLE Kommentare zurückgibt.
Enrico Carlesso
3
Das hat mich für eine Weile wirklich fertig gemacht. Vor allem, wenn Sie dies in eine Klassenmethode wie die folgende def self.random; unscoped.order('rand()'); endeinfügen : unscoped entfernt ALL SQL zuvor, nicht nur das, was unter default_scope aufgeführt ist. Obwohl technisch eine korrekte Antwort, seien Sie vorsichtig mitunstopped
Schneems
7
WARNUNG! Unscoped entfernt NICHT nur das default_scope, es wurde bereits in einem anderen Kommentar gesagt, aber es kann wirklich durcheinander bringen.
Dsimard
15
Eine gute Faustregel ist, nur unscopedwenn es einem Modell direkt folgen kann, zB Foo.unscoped.blah()ist es ok aber nie Foo.blah().unscoped.
Grant Birchmeier
stackoverflow.com/questions/1834159/… umgeht den von Enrico erwähnten Nebeneffekt
wbharding
107

Wenn Sie nur die in definierte Reihenfolge ändern müssen default_scope, können Sie die reorderMethode verwenden .

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

Foo.reorder('created_at asc')

führt die folgende SQL aus:

SELECT * FROM "foos" ORDER BY created_at asc
GuiGS
quelle
3
Tipp: Definieren Sie einen Bereich wie scope :without_default_order, -> { reorder("") }und Sie können Dinge wie tun. Foo.without_default_order.order("created_at ASC")In einigen Situationen liest es sich besser (vielleicht nicht genau diese Situation, aber ich hatte eine).
Henrik N
Nachbestellung hat es für mich getan. Vielen Dank!
Andre Zimpel
49

Da 4.1Sie verwenden können ActiveRecord::QueryMethods#unscope, um Standardbereich zu bekämpfen:

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

Es ist derzeit möglich, unscopeDinge wie : :where, :select, :group, :order, :lock, :limit, :offset, :joins, :includes, :from, :readonly, :having.

Aber bitte vermeiden default_scopeSie es trotzdem , wenn Sie können . Es ist zu deinem Besten.

Jibiel
quelle
Diese Antwort sollte höher sein
Stephen Corwin
5

Rails 3 default_scope scheint nicht wie in Rails 2 überschrieben zu werden.

z.B

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"

In meiner App mit PostgreSQL GEWINNT die Reihenfolge im Standardbereich. Ich entferne alle meine default_scopes und codiere sie explizit überall ein.

Fallstricke Rails3!

Vanboom
quelle
1
Sie müssen verwendenBar.foos.reorder(:created_at => :asc)
Ivan Stana
5

Mit Rails 3+ können Sie eine Kombination aus unscoped und merge verwenden:

# model User has a default scope
query = User.where(email: "[email protected]")

# get rid of default scope and then merge the conditions
query = query.unscoped.merge(query)
Santervo
quelle
Dies funktionierte auch für mich, um zuerst unscoped (Rails 4.2) anzurufen:User.unscoped.where(email: "[email protected]")
Nick
3

Auf Rails 5.1+ (und vielleicht früher, aber ich habe getestet, dass es unter 5.1 funktioniert) ist es möglich, eine bestimmte Spalte zu entfernen. Dies ist imho die ideale Lösung, um eine Spalte default_scopeauf eine Weise zu entfernen , die innerhalb eines benannten Bereichs verwendet werden kann. Im Fall der OPs default_scope,

Foo.unscope(where: :bar)

Oder

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

Beides führt zu einer SQL-Abfrage, die nicht den ursprünglichen Bereich anwendet, aber alle anderen Bedingungen anwendet, die mit dem Arel zusammengeführt werden.

wbharding
quelle
2

Nun, Sie können immer den alten Favoriten find_by_sqlmit der vollständigen Abfrage verwenden. Zum Beispiel: Model.find_by_sql ("SELECT * FROM models WHERE id = 123")

Ady Rosen
quelle