Quando utilizzare una relazione "has_many: through" in Rails?


123

Sto cercando di capire cos'è has_many :throughe quando usarlo (e come). Tuttavia, non lo capisco. Sto leggendo Beginning Rails 3 e ho provato a usare Google, ma non riesco a capire.

Risposte:


177

Supponi di avere due modelli: Usere Group.

Se desideri che gli utenti appartengano a gruppi, potresti fare qualcosa del genere:

class Group < ActiveRecord::Base
  has_many :users
end

class User < ActiveRecord::Base
  belongs_to :group
end

E se volessi monitorare metadati aggiuntivi attorno all'associazione? Ad esempio, quando l'utente si è unito al gruppo o forse qual è il ruolo dell'utente nel gruppo?

Qui è dove rendi l'associazione un oggetto di prima classe:

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

  # has attributes for date_joined and role
end

Ciò introduce una nuova tabella ed elimina la group_idcolonna dalla tabella dell'utente.

Il problema con questo codice è che dovresti aggiornare ogni dove usi la classe utente e cambiarla:

user.groups.first.name

# becomes

user.group_memberships.first.group.name

Questo tipo di codice fa schifo e rende doloroso introdurre cambiamenti come questo.

has_many :through ti offre il meglio di entrambi i mondi:

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

Ora puoi trattarlo come un normale has_many, ma ottieni il vantaggio del modello di associazione quando ne hai bisogno.

Nota che puoi farlo anche con has_one.

Modifica: semplificare l'aggiunta di un utente a un gruppo

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

2
Qui è dove aggiungerei un metodo sul usermodello per aggiungere il gruppo. Qualcosa come la modifica che ho appena fatto. Spero che questo ti aiuti.
Ben Scheirman

Capisco, devo scrivere anche io user.groups << group? O è tutto gestito da questa associazione?
LuckyLuke

Ho una classe che è la stessa di group_membership e ho problemi a usare factory girl con essa mi aiuteresti?
Ayman Salah

Io userei "has_many: group_memberships". L'utilizzo del singolare funzionerà, ma user.group_membership sarà una raccolta e user.group_memberships non funzionerà.
calasyr

È stato un errore di battitura. Fisso.
Ben Scheirman

212

Supponi di avere questi modelli:

Car
Engine
Piston

Un'auto has_one :engine
Un motore belongs_to :car
Un motorehas_many :pistons
Pistonebelongs_to :engine

Un'automobile has_many :pistons, through: :engine
pistone perhas_one :car, through: :engine

Essenzialmente stai delegando una relazione di modello a un altro modello, quindi invece di dover chiamare car.engine.pistons, puoi semplicemente farlocar.pistons


9
Questo ha molto senso!
RajG

24
Lo chiamo SACA - ShortAndClearAnswer.
Asme solo il

18

ActiveRecord Unisci tabelle

has_many :throughe le has_and_belongs_to_manyrelazioni funzionano tramite una tabella di join , che è una tabella intermedia che rappresenta la relazione tra altre tabelle. A differenza di una query JOIN, i dati vengono effettivamente archiviati in una tabella.

Differenze pratiche

Con has_and_belongs_to_many, non è necessaria una chiave primaria e si accede ai record tramite le relazioni ActiveRecord anziché tramite un modello ActiveRecord. Di solito usi HABTM quando desideri collegare due modelli con una relazione molti-a-molti.

Si utilizza una has_many :throughrelazione quando si desidera interagire con la tabella di join come un modello Rails, completo di chiavi primarie e la possibilità di aggiungere colonne personalizzate ai dati uniti. Quest'ultimo è particolarmente importante per i dati che sono rilevanti per le righe unite, ma non appartengono realmente ai modelli correlati, ad esempio la memorizzazione di un valore calcolato derivato dai campi nella riga unita.

Guarda anche

In A Guide to Active Record Associations , la raccomandazione recita:

La regola pratica più semplice è che dovresti impostare una relazione has_many: through se hai bisogno di lavorare con il modello di relazione come entità indipendente. Se non è necessario eseguire alcuna operazione con il modello di relazione, potrebbe essere più semplice impostare una relazione has_and_belongs_to_many (anche se è necessario ricordarsi di creare la tabella di unione nel database).

Dovresti usare has_many: through se hai bisogno di convalide, callback o attributi extra sul modello di join.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.