Warum wird die Verwendung der Schienen default_scope häufig empfohlen?

125

Überall auf der Internet - Leute erwähnen , dass die Schienen mit default_scopeist eine schlechte Idee, und die Top - Hits für default_scopeauf Stackoverflow sind darüber , wie es zu überschreiben. Das fühlt sich durcheinander an und verdient eine explizite Frage (glaube ich).

Also: Warum wird die Verwendung der Schienen default_scopegegen empfohlen?

wrtsprt
quelle

Antworten:

192

Problem 1

Betrachten wir das grundlegende Beispiel:

class Post < ActiveRecord::Base
  default_scope { where(published: true) }
end

Die Motivation, die Standardeinstellung published: truefestzulegen, könnte darin bestehen, sicherzustellen, dass Sie explizit sein müssen, wenn Sie unveröffentlichte (private) Beiträge anzeigen möchten. So weit, ist es gut.

2.1.1 :001 > Post.all
  Post Load (0.2ms)  SELECT "posts".* FROM "posts"  WHERE "posts"."published" = 't'

Nun, das ist ziemlich genau das, was wir erwarten. Versuchen wir jetzt:

2.1.1 :004 > Post.new
 => #<Post id: nil, title: nil, published: true, created_at: nil, updated_at: nil>

Und da haben wir das erste große Problem mit dem Standardbereich:

=> default_scope wirkt sich auf Ihre Modellinitialisierung aus

In einer neu erstellten Instanz eines solchen Modells default_scopewird das reflektiert. Während Sie vielleicht sicher sein wollten, nicht unveröffentlichte Beiträge nicht zufällig aufzulisten, erstellen Sie jetzt standardmäßig veröffentlichte Beiträge.

Problem 2

Betrachten Sie ein ausführlicheres Beispiel:

class Post < ActiveRecord::Base
  default_scope { where(published: true) }
  belongs_to :user
end 

class User < ActiveRecord::Base
  has_many :posts
end

Lassen Sie uns die ersten Benutzerbeiträge erhalten:

2.1.1 :001 > User.first.posts
  Post Load (0.3ms)  SELECT "posts".* FROM "posts"  WHERE "posts"."published" = 't' AND "posts"."user_id" = ?  [["user_id", 1]]

Dies sieht wie erwartet aus (stellen Sie sicher, dass Sie ganz nach rechts scrollen, um den Teil über die Benutzer-ID anzuzeigen).

Jetzt möchten wir die Liste aller Beiträge - einschließlich unveröffentlichter - erhalten, beispielsweise für die Ansicht des angemeldeten Benutzers. Sie werden feststellen, dass Sie den Effekt von "überschreiben" oder "rückgängig machen" müssen default_scope. Nach einem kurzen Google werden Sie wahrscheinlich herausfinden unscoped. Sehen Sie, was als nächstes passiert:

2.1.1 :002 > User.first.posts.unscoped
  Post Load (0.2ms)  SELECT "posts".* FROM "posts"

=> Unscoped entfernt ALLE Bereiche, die normalerweise für Ihre Auswahl gelten, einschließlich (aber nicht beschränkt auf) Zuordnungen.

Es gibt mehrere Möglichkeiten, die verschiedenen Effekte von zu überschreiben default_scope. Das richtig zu machen wird sehr schnell kompliziert und ich würde argumentieren default_scope, dass es eine sicherere Wahl wäre, das überhaupt nicht zu verwenden .

wrtsprt
quelle
2
Zum Anhäufen: Das einzige Mal, dass ich default_scope nützlich fand, war, wenn Sie unbedingt einige Assoziationen standardmäßig laden möchten. default_scope {eifrige_Ladung ([: Kategorie ,: Kommentare])}. Jedoch!!! Wenn Sie für dieses Modell wie Product.count eine Zählabfrage durchführen, werden die Zuordnungen für alle Produkte eifrig geladen. Und wenn Sie 50.000 Datensätze haben, ist Ihre Zählabfrage nur von 15 ms auf 500 ms gestiegen, denn während Sie nur zählen möchten, bleibt Ihr default_scope bei allem anderen.
Konung
16
Mir scheint, dass das Problem mit unscopedstatt default_scopein Problem # 2
Kapitän Fogetti
4
@ CaptainFogetti In der Tat. Ich halte es immer noch für eine gute Idee, die Nachteile von unscoped als möglichen Nachteil von default_scope darzustellen. In den meisten nicht trivialen Fällen führt die Verwendung von default_scope dazu, dass Sie unscoped verwenden müssen. Dies ist eine Einschränkung zweiten Grades (mangels eines besseren Begriffs), die bei der Erforschung einer Methode leicht zu übersehen ist.
Wrtsprt
1
Das Problem mit dem Anwendungsfall in Ihrer Antwort ist, dass es viele Fälle gibt, in denen Sie unveröffentlichte Beiträge suchen möchten. In der Tat würde ich argumentieren, dass das Finden veröffentlichter Beiträge ein Sonderfall ist. Sie möchten nur veröffentlichte Beiträge veröffentlichen, wenn jemand die öffentliche Seite anzeigt. Es gibt jedoch viele Fälle, in denen Sie unveröffentlichte Beiträge sehen möchten.
B Sieben
3
Ich denke, eine gute Verwendung default_scopeist, wenn Sie möchten, dass etwas sortiert wird : default_scope { order(:name) }.
user2985898
9

Ein weiterer Grund, den Sie nicht verwenden sollten, default_scopeist das Löschen einer Instanz eines Modells, die eine Beziehung von 1 zu vielen zum default_scopeModell hat

Betrachten Sie zum Beispiel:

    class User < ActiveRecord::Base
      has_many :posts, dependent: :destroy
    end 

    class Post < ActiveRecord::Base
      default_scope { where(published: true) }
      belongs_to :user
    end

Durch das Anrufen user.destroywerden alle Beiträge gelöscht published, aber keine Beiträge unpublished. Daher löst die Datenbank eine Fremdschlüsselverletzung aus, da sie Datensätze enthält, die auf den Benutzer verweisen, den Sie entfernen möchten.

Koekenbakker28
quelle
6

default_scope wird häufig empfohlen, da es manchmal fälschlicherweise verwendet wird, um die Ergebnismenge einzuschränken. Eine gute Verwendung von default_scope besteht darin, die Ergebnismenge zu ordnen.

Ich würde mich von der Verwendung wherein default_scope fernhalten und lieber einen Bereich dafür erstellen.

nahankid
quelle
1
Das zweite Problem "Unscoped entfernt ALLE Bereiche, die normalerweise für Ihre Auswahl gelten, einschließlich (aber nicht beschränkt auf) Zuordnungen" besteht weiterhin, selbst wenn der default_scopeeinzige enthält order. Dieses Verhalten von unscopedist ziemlich unerwartet.
Zack Xu
1

Für mich ist nicht eine schlechte Idee , muss aber mit Vorsicht verwendet werden !. Es gibt einen Fall, in dem ich immer bestimmte Datensätze ausblenden wollte, wenn ein Feld festgelegt wurde.

  1. Vorzugsweise ist die default_scopemuß mit dem DB - Standardwert übereinstimmt (zB { where(hidden_id: nil) })
  2. Wenn Sie absolut sicher sind, dass Sie diese Datensätze anzeigen möchten, gibt es immer die unscopedMethode, mit der Sie dies vermeiden könnendefault_scope

Es wird also darauf ankommen und die wirklichen Bedürfnisse.

Sposmen
quelle
0

Ich finde default_scopees nur nützlich, einige Parameter so zu ordnen, dass sie in jeder Situation sind ascoder descin jeder Reihenfolge. Ansonsten vermeide ich es wie eine Pest

Moses Liao GZ
quelle