Risposte:
Supponi di avere due modelli: User
e 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_id
colonna 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
user.groups << group
? O è tutto gestito da questa associazione?
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
has_many :through
e le has_and_belongs_to_many
relazioni 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.
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 :through
relazione 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.
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.
user
modello per aggiungere il gruppo. Qualcosa come la modifica che ho appena fatto. Spero che questo ti aiuti.