web-dev-qa-db-fra.com

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?

109
wrtsprt

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.

181
wrtsprt

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.

4
nahankid

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.

3
Koekenbakker28

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

0
Moses Liao GZ

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.

  1. 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) })
  2. Lorsque vous êtes absolument certain de vouloir afficher ces enregistrements, il existe toujours la méthode unscoped qui évite votre default_scope.

Cela dépendra donc des besoins réels.

0
Sposmen