Ruby on rails-référence le même modèle deux fois?

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

Par exemple, si j'avais un User modèle PrivateMessage modèle, la table pm devrait garder la trace des deux sender et recipient.

évidemment, pour une seule relation, je ferais simplement ceci:

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

Existe-t-il une façon similaire d'établir deux relations?

aussi, est - ce qu'il y a de toute façon à mettre en place des alias pour le les relations?

Donc plutôt que de dire:

@message.user

Vous pouvez utiliser quelque chose comme:

@message.sender ou @message.recipient

Tout conseil serait grandement apprécié.

Merci.

42
demandé sur aceofbassgreg 2010-01-13 17:16:15

4 réponses

Ajouter à votre Modèle

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

Et vous êtes en mesure d'appeler @message.sender et @message.recipient et les deux références au modèle Utilisateur.

au Lieu de user:references dans votre commande generate vous avez besoin de sender:references et recipient:references

51
répondu Veger 2014-03-03 20:05:05

Voici une réponse complète à cette question, au cas où les gens qui visitent cette question sont nouveaux à Ruby on Rails et ont du mal à tout assembler (comme je l'étais lorsque j'ai examiné cette question pour la première fois).

certaines parties de la solution ont lieu dans vos Migrations et certaines 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 références à un autre tableau. Les Rails vont en fait créer des colonnes appelées 'sender_id' et 'recipient_id' pour vous. Dans notre cas, ils seront chaque ligne de référence dans la table Users, mais nous spécifions que dans les Modèles, pas dans les migrations.

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é :sender, en spécifiant que cette propriété est liée à la classe User. Les Rails, en voyant "belongs_to: sender", chercheront une colonne dans votre base de données appelée "sender_id", que nous avons définie ci-dessus, et l'utiliser pour stocker la clé étrangère. Alors que vous faites exactement la même chose pour le destinataire.

cela vous permettra d'accéder à votre expéditeur et à votre destinataire, les deux instances du modèle utilisateur, à travers 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é: 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". Alors 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

faire l'une ou l'autre de ces options va retourner un tableau d'instances du modèle PrivateMessage.

....

101
répondu Richard Jones 2018-06-30 22:19:42

salut pour avoir les deux côtés de la relation de faire comme ci-dessous dans votre 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 aidera...

16
répondu radmehr 2011-05-23 12:43:59

Les réponses ci-dessus, tandis que d'excellentes, de ne pas créer de contraintes de clés étrangères dans la base de données, au lieu seulement de créer des index et des colonnes de type 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

ceci assurera que les indices sont 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.

8
répondu bbengfort 2017-09-13 01:50:57