web-dev-qa-db-fra.com

Ruby on Rails - Référencez deux fois le même modèle?

Est-il possible de mettre en place une double relation dans les modèles activerecord via le generate scaffold commande?

Par exemple, si j'avais un modèle User et un modèle PrivateMessage, la table private_messages aurait besoin de garder une trace à la fois des sender et recipient.

Évidemment, pour une seule relation, je ferais juste ceci:

Ruby script/generate scaffold pm title:string content:string user:references

Existe-t-il une manière similaire d'établir deux relations?

De plus, existe-t-il de toute façon des alias pour les relations?

Donc plutôt que de dire:

@message.user

Vous pouvez utiliser quelque chose comme:

@message.sender ou @message.recipient

Tout avis serait grandement apprécié.

Merci.

51
Dan

Ajoutez ceci à votre modèle

has_one :sender, :class_name => "User"
has_one :recipient, :class_name => "User"

Et vous pouvez appeler @message.sender et @message.recipient et les deux font référence au modèle utilisateur.

Au lieu de user:references dans votre commande de génération dont vous auriez besoin sender:references et recipient:references

56
Veger

Voici une réponse complète à ce problème, au cas où les personnes visitant cette question sont nouvelles à Ruby on Rails et ont du mal à tout assembler (comme j'étais la première fois que j’ai examiné cela).

Certaines parties de la solution ont lieu dans vos migrations et d'autres dans vos modèles:

Migrations

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

Ici, vous spécifiez qu'il y a deux colonnes dans ce tableau qui seront appelées: expéditeur et: destinataire et qui contiennent des références à un autre tableau. Rails créera en fait des colonnes appelées 'sender_id' et 'recipient_id' pour vous. Dans notre cas, elles référenceront chacune des lignes dans la table Users, mais nous spécifions que dans les modèles, pas dans les migrations .

Des modèles

class PrivateMessage < ActiveRecord::Base
  belongs_to :sender, :class_name => 'User'
  belongs_to :recipient, :class_name => 'User'
end

Ici, vous créez une propriété sur le modèle PrivateMessage nommée: sender, puis spécifiez que cette propriété est liée à la classe User. Rails, voyant le "appartient à: expéditeur", recherchera une colonne dans votre base de données appelée "sender_id", que nous avons définie ci-dessus, et l'utilisera pour stocker la clé étrangère. Ensuite, vous faites exactement la même chose pour le destinataire.

Cela vous permettra d'accéder à votre expéditeur et destinataire, les deux instances du modèle utilisateur, via une instance du modèle PrivateMessage, comme ceci:

@private_message.sender.name
@private_message.recipient.email

Voici votre modèle d'utilisateur:

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

Ici, vous créez une propriété sur le modèle utilisateur nommée: sent_private_messages, en spécifiant que cette propriété est liée au modèle PrivateMessage et que la clé étrangère sur le modèle PrivateMessage qui la relie à cette propriété est appelée 'sender_id'. Ensuite, vous faites la même chose pour les messages privés reçus.

Cela vous permet d'obtenir tous les utilisateurs envoyés ou reçus des messages privés en faisant quelque chose comme ceci:

@user.sent_private_messages
@user.received_private_messages

Si vous effectuez l'une de ces opérations, vous retournerez un tableau d'instances du modèle PrivateMessage.

....

117
Richard Jones

salut pour que les deux relations latérales fassent comme ci-dessous dans vos deux modèles:

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

J'espère que cela vous aide ...

18
radmehr

Les réponses ci-dessus, bien qu'excellentes, ne créent pas de contraintes de clé étrangère dans la base de données, mais créent uniquement des index et des colonnes bigint. Pour vous assurer que la contrainte de clé étrangère est appliquée, ajoutez ce qui suit à votre migration:

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

Cela garantira que les indices seront créés sur le sender_id et recipient_id ainsi que les contraintes de clé étrangère dans la base de données que vous utilisez.

9
bbengfort