web-dev-qa-db-fra.com

Quand faut-il utiliser une relation "has_many: through" dans Rails?

J'essaie de comprendre ce que has_many :through est et quand l'utiliser (et comment). Cependant, je ne comprends pas. Je lis Début Rails 3 et j'ai essayé Google, mais je ne parviens pas à comprendre.

115
LuckyLuke

Supposons que vous ayez deux modèles: User et Group.

Si vous souhaitez que les utilisateurs appartiennent à des groupes, vous pouvez faire quelque chose comme ceci:

class Group < ActiveRecord::Base
  has_many :users
end

class User < ActiveRecord::Base
  belongs_to :group
end

Et si vous vouliez suivre des métadonnées supplémentaires autour de l'association? Par exemple, lorsque l'utilisateur a rejoint le groupe, ou peut-être quel est le rôle de l'utilisateur dans le groupe?

C'est là que vous faites de l'association un objet de première classe:

class GroupMembership < ActiveRecord::Base
  belongs_to :user
  belongs_to :group

  # has attributes for date_joined and role
end

Cela introduit une nouvelle table et élimine la colonne group_id De la table de l'utilisateur.

Le problème avec ce code est que vous devez mettre à jour chaque fois que vous utilisez la classe user et la changez:

user.groups.first.name

# becomes

user.group_memberships.first.group.name

Ce type de code est nul, et il introduit des changements comme ceux-ci douloureux.

has_many :through vous donne le meilleur des deux mondes:

class User < ActiveRecord::Base
  has_many :groups, :through => :group_memberships  # Edit :needs to be plural same as the has_many relationship   
  has_many :group_memberships
end

Vous pouvez maintenant le traiter comme un has_many Normal, mais bénéficiez du modèle d'association lorsque vous en avez besoin.

Notez que vous pouvez également le faire avec has_one.

Édition: faciliter l'ajout d'un utilisateur à un groupe

def add_group(group, role = "member")
  self.group_associations.build(:group => group, :role => role)
end
170
Ben Scheirman

Disons que vous avez ces modèles:

Car
Engine
Piston

Une voiture has_one :engine
Un moteur belongs_to :car
Un moteur has_many :pistons
Piston belongs_to :engine

Une voiture has_many :pistons, through: :engine
Piston has_one :car, through: :engine

En gros, vous déléguez une relation de modèle à un autre modèle. Par conséquent, au lieu de devoir appeler car.engine.pistons, tu peux juste faire car.pistons

187
cpuguy83

Tables de jointure ActiveRecord

has_many :through et has_and_belongs_to_many Les relations fonctionnent via une table de jointure , qui est une table intermédiaire représentant la relation entre les autres tables. Contrairement à une requête JOIN, les données sont en réalité stockées dans une table.

Différences Pratiques

Avec has_and_belongs_to_many, vous n'avez pas besoin de clé primaire et vous accédez aux enregistrements par le biais de relations ActiveRecord plutôt que par le biais d'un modèle ActiveRecord. Vous utilisez généralement HABTM lorsque vous souhaitez lier deux modèles avec une relation plusieurs à plusieurs.

Vous utilisez un has_many :through relation lorsque vous souhaitez interagir avec la table de jointure en tant que modèle Rails, complet avec des clés primaires et la possibilité d'ajouter des colonnes personnalisées aux données jointes. Cette dernière est particulièrement importante pour les données qui est pertinent pour les lignes jointes, mais n'appartient pas vraiment aux modèles associés - par exemple, le stockage d'une valeur calculée dérivée des champs de la ligne jointe.

Voir également

Dans Guide des associations d’enregistrements actifs , la recommandation est la suivante:

La règle la plus simple est de définir une relation has_many: through si vous devez utiliser le modèle de relation en tant qu'entité indépendante. Si vous n’avez rien à faire avec le modèle de relation, il peut être plus simple de configurer une relation has_and_belongs_to_many (vous devrez toutefois vous rappeler de créer la table de jointure dans la base de données).

Vous devez utiliser has_many: through si vous avez besoin de validations, de rappels ou d'attributs supplémentaires sur le modèle de jointure.

16
Todd A. Jacobs