Wann sollte eine "has_many: through" -Relation in Rails verwendet werden?

123

Ich versuche zu verstehen, was has_many :throughund wann ich es verwenden soll (und wie). Ich verstehe es jedoch nicht. Ich lese Beginning Rails 3 und habe versucht zu googeln, kann es aber nicht verstehen.

Lucky Luke
quelle

Antworten:

177

Angenommen, Sie haben zwei Modelle: Userund Group.

Wenn Sie möchten, dass Benutzer zu Gruppen gehören, können Sie Folgendes tun:

class Group < ActiveRecord::Base
  has_many :users
end

class User < ActiveRecord::Base
  belongs_to :group
end

Was ist, wenn Sie zusätzliche Metadaten rund um die Assoziation verfolgen möchten? Zum Beispiel, wenn der Benutzer der Gruppe beigetreten ist oder welche Rolle der Benutzer in der Gruppe spielt?

Hier machen Sie die Assoziation zu einem erstklassigen Objekt:

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

  # has attributes for date_joined and role
end

Dadurch wird eine neue Tabelle eingeführt und die group_idSpalte aus der Benutzertabelle entfernt.

Das Problem mit diesem Code ist, dass Sie jeden anderen Ort, an dem Sie die Benutzerklasse verwenden, aktualisieren und ändern müssen:

user.groups.first.name

# becomes

user.group_memberships.first.group.name

Diese Art von Code ist zum Kotzen und macht das Einführen solcher Änderungen schmerzhaft.

has_many :through gibt Ihnen das Beste aus beiden Welten:

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

Jetzt können Sie es wie ein normales behandeln has_many, aber Sie können das Assoziationsmodell bei Bedarf nutzen.

Beachten Sie, dass Sie dies auch mit tun können has_one.

Bearbeiten: Erleichtert das Hinzufügen eines Benutzers zu einer Gruppe

def add_group(group, role = "member")
  self.group_associations.build(:group => group, :role => role)
end
Ben Scheirman
quelle
2
Hier würde ich dem userModell eine Methode hinzufügen, um die Gruppe hinzuzufügen. So etwas wie die Bearbeitung, die ich gerade gemacht habe. Hoffe das hilft.
Ben Scheirman
Ich verstehe, muss ich auch schreiben user.groups << group? Oder wird alles von diesem Verein erledigt?
LuckyLuke
Ich habe eine Klasse, die mit der group_membership identisch ist, und ich habe Probleme mit der Verwendung von Factory Girl. Würden Sie mir helfen?
Ayman Salah
Ich würde "has_many: group_memberships" verwenden. Die Verwendung des Singulars funktioniert, aber user.group_membership wird eine Sammlung und user.group_memberships funktioniert nicht.
Calasyr
Das war ein Tippfehler. Fest.
Ben Scheirman
212

Angenommen, Sie haben diese Modelle:

Car
Engine
Piston

Ein Auto has_one :engine
Ein Motor belongs_to :car
Ein Motor has_many :pistons
Kolbenbelongs_to :engine

Ein has_many :pistons, through: :engine
Autokolbenhas_one :car, through: :engine

Im Wesentlichen delegieren Sie eine Modellbeziehung an ein anderes Modell, sodass car.engine.pistonsSie dies einfach tun müssen , anstatt anrufen zu müssencar.pistons

cpuguy83
quelle
9
Das macht sehr viel Sinn!
RajG
24
Ich nenne das SACA - ShortAndClearAnswer.
Asme Nur
18

ActiveRecord-Verknüpfungstabellen

has_many :throughund has_and_belongs_to_manyBeziehungen funktionieren über eine Verknüpfungstabelle , bei der es sich um eine Zwischentabelle handelt, die die Beziehung zwischen anderen Tabellen darstellt. Im Gegensatz zu einer JOIN-Abfrage werden Daten tatsächlich in einer Tabelle gespeichert.

Praktische Unterschiede

Mit has_and_belongs_to_manybenötigen Sie keinen Primärschlüssel und greifen über ActiveRecord-Beziehungen und nicht über ein ActiveRecord-Modell auf die Datensätze zu. Normalerweise verwenden Sie HABTM, wenn Sie zwei Modelle mit einer Viele-zu-Viele-Beziehung verknüpfen möchten.

Sie verwenden eine has_many :throughBeziehung, wenn Sie mit der Verknüpfungstabelle als Rails-Modell interagieren möchten. Dazu gehören Primärschlüssel und die Möglichkeit, den verknüpften Daten benutzerdefinierte Spalten hinzuzufügen. Letzteres ist besonders wichtig für Daten, die für die verknüpften Zeilen relevant sind, aber nicht wirklich zu den zugehörigen Modellen gehören - beispielsweise zum Speichern eines berechneten Werts, der aus den Feldern in der verknüpften Zeile abgeleitet wird.

Siehe auch

In einem Handbuch zu aktiven Datensatzzuordnungen lautet die Empfehlung:

Die einfachste Faustregel lautet, dass Sie eine has_many: through-Beziehung einrichten sollten, wenn Sie mit dem Beziehungsmodell als unabhängige Entität arbeiten müssen. Wenn Sie mit dem Beziehungsmodell nichts tun müssen, ist es möglicherweise einfacher, eine Beziehung has_and_belongs_to_many einzurichten (obwohl Sie daran denken müssen, die Verknüpfungstabelle in der Datenbank zu erstellen).

Sie sollten has_many: through verwenden, wenn Sie Validierungen, Rückrufe oder zusätzliche Attribute für das Join-Modell benötigen.

Todd A. Jacobs
quelle