Ruby on rails: fai riferimento allo stesso modello due volte?
-
20-09-2019 - |
Domanda
È possibile impostare una doppia relazione in activerecord
modelli tramite il generate scaffold
comando?
Ad esempio, se avessi un User
modello e a PrivateMessage
modello, la tabella pm dovrebbe tenere traccia di entrambi i file sender
E recipient
.
Ovviamente, per una singola relazione farei semplicemente questo:
ruby script/generate scaffold pm title:string content:string user:references
Esiste un modo simile per impostare due relazioni?
Inoltre, è comunque possibile impostare alias per le relazioni?
Quindi invece di dire:
@message.user
Puoi usare qualcosa come:
@message.sender
O @message.recipient
Qualsiasi consiglio sarebbe molto apprezzato.
Grazie.
Soluzione
Aggiungi questo al vostro modello
has_one :sender, :class_name => "User"
has_one :recipient, :class_name => "User"
E si è in grado di chiamare @message.sender
e @message.recipient
ed entrambi riferimento al modello User.
Invece di user:references
nel comando generare avresti bisogno sender:references
e recipient:references
Altri suggerimenti
Ecco una risposta completa a questo problema, nel caso in cui le persone che visitano questa domanda siano nuove in Ruby on Rails e abbiano difficoltà a mettere tutto insieme (come ho fatto io quando ho esaminato questo problema per la prima volta).
Alcune parti della soluzione si svolgono nelle tue migrazioni e altre nei tuoi modelli:
Migrazioni
class CreatePrivateMessages < ActiveRecord::Migration
def change
create_table :private_messages do |t|
t.references :sender
t.references :recipient
end
# Rails 5+ only: add foreign keys
add_foreign_key :private_messages, :users, column: :sender_id, primary_key: :id
add_foreign_key :private_messages, :users, column: :recipient_id, primary_key: :id
end
end
Qui stai specificando che ci sono due colonne in questa tabella che verranno chiamate :sender e :recipient e che contengono riferimenti a un'altra tabella.Rails creerà effettivamente colonne chiamate 'sender_id' e 'recipient_id' per te.Nel nostro caso ciascuno farà riferimento a righe nella tabella Utenti, ma lo specifichiamo nei modelli, non nelle migrazioni.
Modelli
class PrivateMessage < ActiveRecord::Base
belongs_to :sender, :class_name => 'User'
belongs_to :recipient, :class_name => 'User'
end
Qui stai creando una proprietà sul modello PrivateMessage denominata :sender, quindi specificando che questa proprietà è correlata alla classe User.Rails, vedendo "belongs_to:sender", cercherà una colonna nel tuo database chiamata "sender_id", che abbiamo definito sopra, e la utilizzerà per memorizzare la chiave esterna.Quindi stai facendo esattamente la stessa cosa per il destinatario.
Ciò ti consentirà di accedere al mittente e al destinatario, entrambe le istanze del modello Utente, attraverso un'istanza del modello PrivateMessage, in questo modo:
@private_message.sender.name
@private_message.recipient.email
Ecco il tuo modello utente:
class User < ActiveRecord::Base
has_many :sent_private_messages, :class_name => 'PrivateMessage', :foreign_key => 'sender_id'
has_many :received_private_messages, :class_name => 'PrivateMessage', :foreign_key => 'recipient_id'
end
Qui stai creando una proprietà sul modello utente denominata :sent_private_messages, specificando che questa proprietà è correlata al modello PrivateMessage e che la chiave esterna sul modello PrivateMessage che la collega a questa proprietà è chiamata 'sender_id'.Quindi stai facendo la stessa cosa per i messaggi privati ricevuti.
Ciò ti consente di inviare o ricevere messaggi privati a tutti gli utenti facendo qualcosa del genere:
@user.sent_private_messages
@user.received_private_messages
Eseguendo una di queste operazioni verrà restituita una serie di istanze del modello PrivateMessage.
....
Ciao avere sia relazione lato fare come muggito nelle vostre entrambi i modelli:
class Message < ActiveRecord::Base
belongs_to :sender,
:class_name => "User",
:foreign_key => "sender_id"
belongs_to :recipient,
:class_name => "User",
:foreign_key => "recipient_id"
end
class User < ActiveRecord::Base
has_many :sent,
:class_name => "Message",
:foreign_key => "sent_id"
has_many :received,
:class_name => "Message",
:foreign_key => "received_id"
end
Spero che questo aiuto si ...
Le risposte di cui sopra, mentre eccellente, non creano vincoli stranieri chiave nel database, invece solo gli indici che creano e colonne bigint. Per garantire che il vincolo di chiave esterna viene applicata, aggiungere il seguente alla migrazione:
class CreatePrivateMessages < ActiveRecord::Migration[5.1]
def change
create_table :private_messages do |t|
t.references :sender
t.references :recipient
end
add_foreign_key :private_messages, :users, column: :sender_id, primary_key: :id
add_foreign_key :private_messages, :users, column: :recipient_id, primary_key: :id
end
end
Questo farà sì che gli indici vengono creati sul sender_id
e recipient_id
nonché i vincoli di chiave esterna nel database che si sta utilizzando.