¿Por qué a menudo se recomienda usar los rieles default_scope?

126

Por todas partes en las personas de Internet mencionan que el uso de los carriles default_scopees una mala idea, y los accesos principales para default_scopeel stackoverflow son acerca de cómo sobrescribirlo. Esto se siente en mal estado y merece una pregunta explícita (creo).

Entonces: ¿por qué se default_scoperecomienda usar los rieles ?

wrtsprt
fuente

Respuestas:

192

Problema 1

Consideremos el ejemplo básico:

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

La motivación para establecer el valor predeterminado published: truepodría ser asegurarse de tener que ser explícito cuando desee mostrar publicaciones no publicadas (privadas). Hasta aquí todo bien.

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

Bueno, esto es más o menos lo que esperamos. Ahora intentemos:

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

Y ahí tenemos el primer gran problema con el alcance predeterminado:

=> default_scope afectará la inicialización de su modelo

En una instancia recién creada de dicho modelo, default_scopese reflejará. Entonces, si bien es posible que haya querido asegurarse de no enumerar publicaciones no publicadas por casualidad, ahora está creando las publicadas de forma predeterminada.

Problema 2

Considere un ejemplo más elaborado:

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

class User < ActiveRecord::Base
  has_many :posts
end

Permite obtener las primeras publicaciones de los usuarios:

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

Esto se ve como se esperaba (asegúrese de desplazarse completamente hacia la derecha para ver la parte sobre user_id).

Ahora queremos obtener la lista de todas las publicaciones, incluidas las no publicadas, por ejemplo, para la vista del usuario conectado. Te darás cuenta de que tienes que 'sobrescribir' o 'deshacer' el efecto de default_scope. Después de un rápido google, es probable que lo descubras unscoped. Mira lo que pasa después:

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

=> Sin ámbito elimina TODOS los ámbitos que normalmente podrían aplicarse a su selección, incluidas (entre otras) las asociaciones.

Hay varias formas de sobrescribir los diferentes efectos de default_scope. Hacer eso bien se complica muy rápidamente y argumentaría que no usar el default_scopeen primer lugar, sería una opción más segura.

wrtsprt
fuente
2
Para apilar: la única vez que encontré default_scope útil es cuando absolutamente quieres cargar ansiosamente algunas asociaciones por defecto. default_scope {eager_load ([: category,: comments])}. ¡¡¡Sin embargo!!! Si está haciendo una consulta de conteo en este modelo como Product.count, cargará asociaciones eager_load para todos los productos. Y si tiene 50K registros, su consulta de recuento solo pasó de 15 ms a 500 ms, porque si bien todo lo que desea es contar, su default_scope se unirá a todo lo demás.
Konung
16
Para mí, parece que el problema está en unscopedlugar del default_scopeproblema n. ° 2
Capitán Fogetti el
44
@CaptainFogetti De hecho. Todavía creo que es una buena idea presentar los inconvenientes de unscoped como una posible desventaja de default_scope. En la mayoría de los casos no triviales, el uso de default_scope hará que necesite usar sin ámbito. Esta es una advertencia de segundo grado (a falta de un término mejor), que es fácil pasar por alto al investigar un método.
wrtsprt
1
El problema con el caso de uso en su respuesta es que hay muchos casos en los que desea encontrar publicaciones no publicadas. De hecho, diría que encontrar publicaciones publicadas es un caso especial. La única vez que desea publicaciones publicadas es cuando alguien está viendo la página pública. Pero hay muchas ocasiones en las que quieres ver publicaciones no publicadas.
B Seven
3
Supongo que una buena utilización de default_scopees cuando se quiere algo que debe clasificarse: default_scope { order(:name) }.
user2985898
9

Otra razón para no usar default_scopees cuando está eliminando una instancia de un modelo que tiene una relación de 1 a muchos con el default_scopemodelo

Considere por ejemplo:

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

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

Las llamadas user.destroyeliminarán todas las publicaciones que son published, pero no eliminarán las publicaciones que sí lo sean unpublished. Por lo tanto, la base de datos arrojará una violación de clave foránea porque contiene registros que hacen referencia al usuario que desea eliminar.

Koekenbakker28
fuente
6

default_scope a menudo se recomienda contra porque a veces se usa incorrectamente para limitar el conjunto de resultados. Un buen uso de default_scope es ordenar el conjunto de resultados.

Me mantendría alejado del uso whereen default_scope y más bien crearía un alcance para eso.

nahankid
fuente
1
El segundo problema "Sin ámbito elimina TODOS los ámbitos que normalmente podrían aplicarse a su selección, incluidas (entre otras) las asociaciones" aún existe, incluso si el default_scopeúnico contiene order. Este comportamiento de unscopedes bastante inesperado.
Zack Xu
1

Para mí no es una mala idea, ¡ pero debe usarse con precaución! Hay un caso en el que siempre quise ocultar ciertos registros cuando se establece un campo.

  1. Preferiblemente, el default_scopedebe coincidir con el valor predeterminado DB (por ejemplo: { where(hidden_id: nil) })
  2. Cuando está totalmente seguro de que desea mostrar esos registros, siempre existe el unscopedmétodo que evitará sudefault_scope

Entonces dependerá y las necesidades reales.

Sposmen
fuente
0

Sólo encuentro default_scopepara ser útil sólo en ordenar algunos parámetros para estar en asco descorden en toda situación. De lo contrario lo evito como peste

Moses Liao GZ
fuente