Perché usare le rotaie default_scope spesso sconsiglia?


126

Ovunque sulle le persone internet contare che utilizzando i binari default_scopeè una cattiva idea, e le hit top per default_scopeStackOverflow sono su come sovrascrivere. Questo sembra incasinato e merita una domanda esplicita (credo).

Quindi: perché si default_scopeconsiglia di non utilizzare le guide ?

Risposte:


192

Problema 1

Consideriamo l'esempio di base:

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

La motivazione per rendere l'impostazione predefinita published: true, potrebbe essere quella di assicurarsi di essere esplicitati quando si desidera mostrare post (privati) non pubblicati. Fin qui tutto bene.

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

Bene, questo è praticamente ciò che ci aspettiamo. Ora proviamo:

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

E lì abbiamo il primo grosso problema con ambito predefinito:

=> default_scope influenzerà l'inizializzazione del modello

In un'istanza appena creata di tale modello, default_scopeverrà riflesso. Quindi, anche se potresti voler essere sicuro di non elencare i messaggi non pubblicati per caso, ora stai creando quelli pubblicati per impostazione predefinita.

Problema 2

Considera un esempio più elaborato:

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

class User < ActiveRecord::Base
  has_many :posts
end

Consente di ottenere i post dei primi utenti:

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]]

Sembra previsto (assicurati di scorrere fino a destra per vedere la parte su user_id).

Ora vogliamo ottenere l'elenco di tutti i post - incluso non pubblicato - diciamo per la vista dell'utente che ha effettuato l'accesso. Ti renderai conto che devi "sovrascrivere" o "annullare" l'effetto di default_scope. Dopo un rapido google, probabilmente lo scoprirai unscoped. Guarda cosa succede dopo:

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

=> Unscoped rimuove TUTTI gli ambiti che normalmente potrebbero applicarsi alla tua selezione, incluse (ma non limitate a) associazioni.

Esistono diversi modi per sovrascrivere i diversi effetti di default_scope. Ottenere questo diritto diventa complicato molto rapidamente e direi che non usare default_scopeil primo, sarebbe una scelta più sicura.


2
Per accumulare: l'unica volta che ho trovato utile default_scope è quando vuoi assolutamente caricare alcune associazioni per impostazione predefinita. default_scope {eager_load ([: categoria,: commenti])}. Però!!! Se si sta eseguendo una query di conteggio su questo modello come Product.count, verranno caricate le associazioni per tutti i prodotti. E se hai record da 50K, la tua query di conteggio è passata da 15ms a 500ms, perché mentre tutto ciò che vuoi è contare, il tuo default_scope lascerà unirsi a tutto il resto.
Konung,

16
A me sembra che il problema sia unscopedinvece che default_scopenel problema n. 2
Capitano Fogetti,

4
@CaptainFogetti Effettivamente. Penso ancora che sia una buona idea presentare gli svantaggi di unscoped come un possibile svantaggio di default_scope. Nella maggior parte dei casi non banali, l'utilizzo di default_scope ti porterà a dover usare unscoped. Questo è un avvertimento di secondo grado (in mancanza di un termine migliore), che è facile perdere durante la ricerca di un metodo.
wrtsprt,

1
Il problema con il caso d'uso nella tua risposta è che ci sono molti casi in cui desideri trovare post non pubblicati. In effetti, direi che trovare post pubblicati è un caso speciale. L'unica volta che vuoi post pubblicati è quando qualcuno sta visualizzando la pagina pubblica. Ma ci sono molte volte in cui vuoi vedere post inediti.
B Seven

3
Credo che un buon uso di default_scopeè quando si desidera qualcosa da ordinare: default_scope { order(:name) }.
user2985898

9

Un altro motivo da non usare default_scopeè quando si elimina un'istanza di un modello che ha una relazione da 1 a molte con il default_scopemodello

Considera ad esempio:

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

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

La chiamata user.destroyeliminerà tutti i post che sono published, ma non eliminerà i post che lo sono unpublished. Quindi il database genererà una violazione di chiave esterna perché contiene record che fanno riferimento all'utente che si desidera rimuovere.


6

default_scope è spesso sconsigliato perché a volte viene utilizzato in modo errato per limitare il set di risultati. Un buon uso di default_scope è di ordinare il set di risultati.

Starei lontano dall'uso wherein default_scope e creerei piuttosto un ambito per quello.


1
Il secondo problema "Unscoped rimuove TUTTI gli ambiti che normalmente potrebbero essere applicati alla tua selezione, incluse (ma non limitate a) associazioni" esiste ancora anche se l' default_scopeunico contiene order. Questo comportamento di unscopedè abbastanza inaspettato.
Zack Xu,

1

Per me non è una cattiva idea ma deve essere usato con cautela !. C'è un caso in cui ho sempre voluto nascondere determinati record quando è impostato un campo.

  1. Preferibilmente, default_scopedeve corrispondere al valore predefinito del DB (ad es . { where(hidden_id: nil) }:)
  2. Quando sei assolutamente sicuro di voler mostrare quei record, c'è sempre il unscopedmetodo che ti eviteràdefault_scope

Quindi dipenderà e le esigenze reali.


0

Trovo solo default_scopeutile solo per ordinare alcuni parametri per essere in asco descordine in tutte le situazioni. Altrimenti lo evito come la peste

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.