Ruby on rails — дважды ссылаться на одну и ту же модель?

Можно ли установить двойную связь в моделях activerecord с помощью команды generate scaffold?

Например, если бы у меня была модель User и модель PrivateMessage, таблица private_messages должна была бы отслеживать как sender, так и recipient.

Очевидно, для одного отношения я бы просто сделал это:

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

Есть ли аналогичный способ установить два отношения?

Кроме того, есть ли способ настроить псевдонимы для отношений?

Поэтому вместо того, чтобы сказать:

@message.user

Вы можете использовать что-то вроде:

@message.sender or @message.recipient

Мы будем очень признательны за любые советы.

Спасибо.


person Dan    schedule 13.01.2010    source источник


Ответы (4)


Добавьте это в свою модель

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

И вы можете вызывать @message.sender и @message.recipient и оба ссылаться на модель пользователя.

Вместо user:references в вашей команде генерации вам понадобятся sender:references и recipient:references

person Veger    schedule 13.01.2010

Вот полный ответ на этот вопрос, на случай, если люди, посещающие этот вопрос, являются новичками в 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 и которые содержат ссылки на другую таблицу. Rails фактически создаст для вас столбцы с именами 'sender_id' и 'recipient_id'. В нашем случае каждая из них будет ссылаться на строки в таблице Users, но мы указываем это в моделях, а не в миграциях.

Модели

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

Здесь вы создаете свойство модели PrivateMessage с именем :sender, а затем указываете, что это свойство связано с классом User. Rails, увидев «belongs_to :sender», будет искать в вашей базе данных столбец с именем «sender_id», который мы определили выше, и использовать его для хранения внешнего ключа. Затем вы делаете то же самое для получателя.

Это позволит вам получить доступ к вашему отправителю и получателю, обоим экземплярам модели пользователя, через экземпляр модели PrivateMessage, например:

@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.

....

person Richard Jones    schedule 21.04.2012
comment
Ключи class_name для модели PrivateMessage должны быть символами. Я не могу редактировать это сам, потому что редактирование должно быть не менее 6 символов. - person Tim Fletcher; 20.09.2012
comment
@TimFletcher Во всех примерах на этой странице используются строки, а не символы: api.rubyonrails.org/classes/ActiveRecord/Associations/. Может быть, сообщение to_s отправляется переданному символу (просто угадал)? - person Richard Jones; 06.10.2012
comment
отличный, очень исчерпывающий ответ. В Rails 5 есть обновление, которое, я думаю, может помочь улучшить ваш ответ, обеспечив добавление ограничений внешнего ключа в базу данных. Я включил изменение миграции в ответ ниже, если вы хотите обновить свой ответ. - person bbengfort; 13.09.2017
comment
в качестве дополнительного примечания я столкнулся с ошибками отношения не существует при попытке сделать это - обе таблицы должны уже существовать до добавления внешних ключей. Мне пришлось создать add_foreign_keys в отдельной миграции после создания обеих таблиц в моем случае. - person Alex E; 23.07.2019
comment
В Rails 5 и выше вы можете поместить операторы foreign_key в блок create_table следующим образом: t.references :sender, foreign_key: { to_table: 'users' } - person Toby 1 Kenobi; 02.08.2019

привет, чтобы обе стороны отношения делали, как показано ниже в обеих моделях:

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

Я надеюсь, что это поможет вам...

person radmehr    schedule 23.05.2011

Приведенные выше ответы, хотя и превосходны, не создают ограничений внешнего ключа в базе данных, а создают только индексы и столбцы 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_id и recipient_id, а также ограничений внешнего ключа в используемой вами базе данных.

person bbengfort    schedule 13.09.2017
comment
это правильное решение для рельсов 5 - person Jacka; 04.03.2018