Pourquoi l'utilisation de Rails est-elle souvent déconseillée par défaut?
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?
Problème 1
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.
Problème 2
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.
- De préférence, le
default_scope
doit correspondre à la valeur par défaut de la base de données (par exemple:{ where(hidden_id: nil) }
) - Lorsque vous êtes absolument certain de vouloir afficher ces enregistrements, il existe toujours la méthode
unscoped
qui évite votredefault_scope
.
Cela dépendra donc des besoins réels.