Ü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?


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.


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)

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

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.


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!


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: "foo@example.com")

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

Dies funktionierte auch für mich, um zuerst unscoped (Rails 4.2) anzurufen:User.unscoped.where(email: "foo@example.com")
— 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.


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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.