web-dev-qa-db-ja.com

Ruby on Rails-同じモデルを2回参照しますか?

generate scaffoldコマンドを使用してactiverecordモデルに二重の関係を設定することはできますか?

たとえば、UserモデルとPrivateMessageモデルがある場合、private_messagesテーブルはsenderrecipientの両方を追跡する必要があります。

明らかに、単一の関係については、これを行うだけです。

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

2つの関係を設定する同様の方法はありますか?

また、関係にエイリアスを設定する方法はありますか?

だから言うよりも:

@message.user

あなたは次のようなものを使うことができます:

@message.senderまたは@message.recipient

何かアドバイスをいただければ幸いです。

ありがとう。

51
Dan

これをモデルに追加します

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

また、@message.sender@message.recipientを呼び出すことができ、どちらもUserモデルを参照します。

生成コマンドのuser:referencesの代わりに、sender:referencesrecipient:referencesが必要になります

56
Veger

この質問にアクセスした人がRuby on Railsに不慣れで、すべてをまとめるのに苦労した場合に備えて)私がこれを最初に調べたとき)。

ソリューションの一部はマイグレーションで行われ、一部はモデルで行われます。

マイグレーション

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

ここでは、このテーブルに:senderおよび:recipientと呼ばれ、別のテーブルへの参照を保持する2つの列があることを指定しています。 Railsは実際には「sender_id」と「recipient_id」という列を作成します。この場合、これらはそれぞれUsersテーブルの行を参照しますが、マイグレーションではなくモデルで指定します。

モデル

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

ここでは、:senderという名前のPrivateMessageモデルにプロパティを作成し、このプロパティがUserクラスに関連していることを指定しています。 Railsは、「belongs_to:sender」を参照して、上記で定義した「sender_id」と呼ばれるデータベース内の列を探し、それを使用して外部キーを格納します。次に、受信者に対してまったく同じことを行います。

これにより、次のように、PrivateMessageモデルのインスタンスを介して、Userモデルの両方のインスタンスであるSenderとRecipientにアクセスできます。

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

これがユーザーモデルです。

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

ここでは、:sent_private_messagesという名前のユーザーモデルのプロパティを作成し、このプロパティがPrivateMessageモデルに関連していること、およびこのプロパティに関連するPrivateMessageモデルの外部キーが 'sender_id'であることを指定しています。次に、受信したプライベートメッセージに対して同じことを行います。

これにより、次のような操作を行うことで、すべてのユーザーがプライベートメッセージを送受信できるようになります。

@user.sent_private_messages
@user.received_private_messages

これらのいずれかを実行すると、PrivateMessageモデルのインスタンスの配列が返されます。

....

117
Richard Jones

こんにちは、両方のモデルで次のように両側の関係を実行します。

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

これがお役に立てば幸いです...

18
radmehr

上記の回答は優れていますが、データベースに外部キー制約を作成せず、インデックスとbigint列のみを作成します。外部キー制約が確実に適用されるようにするには、移行に以下を追加します。

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

これにより、sender_idrecipient_id、および使用しているデータベースの外部キー制約にインデックスが確実に作成されます。

9
bbengfort