Partoutsurles Internet, les gens mentionnent qu'utiliser le code Rails default_scope
est une mauvaise idée et que les meilleurs résultats de default_scope
sur stackoverflow concernent la façon de l'écraser. Cela semble dérangé et mérite une question explicite (je pense).
Alors: pourquoi utiliser le default_scope
de Rails est-il une si mauvaise idée?
Considérons l'exemple de base:
class Post < ActiveRecord::Base
default_scope { where(published: true) }
end
La motivation pour définir le published: true
par défaut peut être de s'assurer que vous devez être explicite lorsque vous souhaitez afficher des publications (non publiées) non publiées. Jusqu'ici tout va bien.
2.1.1 :001 > Post.all
Post Load (0.2ms) SELECT "posts".* FROM "posts" WHERE "posts"."published" = 't'
Eh bien c'est à peu près ce que nous attendons. Essayons maintenant:
2.1.1 :004 > Post.new
=> #<Post id: nil, title: nil, published: true, created_at: nil, updated_at: nil>
Et là nous avons le premier gros problème avec la portée par défaut:
=> default_scope affectera l'initialisation de votre modèle
Dans une instance récemment créée d'un tel modèle, le default_scope
sera reflété. Ainsi, alors que vous souhaitiez vous assurer de ne pas répertorier les publications non publiées par hasard, vous créez maintenant celles publiées par défaut.
Prenons un exemple plus élaboré:
class Post < ActiveRecord::Base
default_scope { where(published: true) }
belongs_to :user
end
class User < ActiveRecord::Base
has_many :posts
end
Permet de récupérer les premiers messages des utilisateurs:
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]]
Cela ressemble à ce que vous attendiez (assurez-vous de faire défiler tout le chemin à droite pour voir la partie concernant l’utilisateur_id).
Maintenant, nous voulons obtenir la liste de tous les messages - non publiés inclus - disons pour la vue de l'utilisateur connecté. Vous vous rendrez compte que vous devez "écraser" ou "annuler" l'effet de default_scope
. Après une rapide recherche sur Google, vous en apprendrez probablement plus sur unscoped
. Voir ce qui se passe ensuite:
2.1.1 :002 > User.first.posts.unscoped
Post Load (0.2ms) SELECT "posts".* FROM "posts"
=> Unscoped supprime TOUTES les étendues qui pourraient normalement s'appliquer à votre sélection, y compris (sans toutefois s'y limiter) les associations.
Il y a plusieurs façons de remplacer les différents effets du default_scope
. Obtenir cela devient compliqué très rapidement et je dirais que ne pas utiliser le default_scope
en premier lieu serait un choix plus sûr.
default_scope est souvent recommandé car il est parfois utilisé de manière incorrecte pour limiter le jeu de résultats. Une bonne utilisation de default_scope consiste à ordonner le jeu de résultats.
J'éviterais d'utiliser where
dans default_scope et créerais plutôt une portée pour cela.
Une autre raison de ne pas utiliser default_scope
est lorsque vous supprimez une instance d'un modèle qui a une relation 1 à plusieurs avec le modèle default_scope
Considérons par exemple:
class User < ActiveRecord::Base
has_many :posts, dependent: :destroy
end
class Post < ActiveRecord::Base
default_scope { where(published: true) }
belongs_to :user
end
L'appel de user.destroy
supprimera tous les articles qui sont published
, mais ne supprimera pas les articles qui sont unpublished
. Par conséquent, la base de données générera une violation de clé étrangère car elle contient des enregistrements faisant référence à l'utilisateur que vous souhaitez supprimer.
Je trouve que default_scope
n'est utile que pour ordonner certains paramètres dans l'ordre asc
ou desc
dans toutes les situations. Sinon je l'évite comme une peste
Pour moi, c'est pas a mauvaise idée mais doit être utilisé avec prudence !. Il y a un cas où j'ai toujours voulu cacher certains enregistrements lorsqu'un champ est défini.
default_scope
doit correspondre à la valeur par défaut de la base de données (par exemple: { where(hidden_id: nil) }
)unscoped
qui évite votre default_scope
.Cela dépendra donc des besoins réels.