Ruby on rails - 两次引用同一个模型?

Posted

技术标签:

【中文标题】Ruby on rails - 两次引用同一个模型?【英文标题】:Ruby on rails - Reference the same model twice? 【发布时间】:2011-01-04 15:49:39 【问题描述】:

是否可以通过generate scaffold 命令在activerecord 模型中设置双重关系?

例如,如果我有一个 User 模型和一个 PrivateMessage 模型,则 private_messages 表需要同时跟踪 senderrecipient

显然,对于单一的关系,我会这样做:

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

有没有类似的方法来建立两个关系?

另外,是否可以为关系设置别名?

所以与其说:

@message.user

你可以使用类似的东西:

@message.sender@message.recipient

任何建议将不胜感激。

谢谢。

【问题讨论】:

【参考方案1】:

这里是这个问题的完整答案,以防访问这个问题的人是 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 模型的实例数组。

....

【讨论】:

class_name 模型的 PrivateMessage 键应该是符号我自己无法编辑,因为编辑必须至少有 6 个字符。 @TimFletcher 此页面上的示例都使用字符串,而不是符号:api.rubyonrails.org/classes/ActiveRecord/Associations/…。也许to_s 消息被发送到传递的符号(只是在这里猜测)? 优秀,非常全面的答案。 Rails 5 有一个更新,我认为它可以帮助您改进答案——确保将外键约束添加到数据库中。如果您想用它更新您的答案,我在下面的答案中包含了对迁移的编辑。 作为附加说明,我在尝试此操作时遇到了“关系不存在”错误 - 在添加外键之前,两个表都必须已经存在。在我的案例中创建两个表后,我必须在单独的迁移中创建 add_foreign_keys 在 Rails 5 及更高版本中,您可以将 foreign_key 语句放入 create_table 块中,如下所示:t.references :sender, foreign_key: to_table: 'users' 【参考方案2】:

将此添加到您的模型中

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

您可以调用 @message.sender@message.recipient 并且都引用 User 模型。

您需要 sender:referencesrecipient:references 而不是生成命令中的 recipient:references

【讨论】:

【参考方案3】:

你好 让双方关系在你的两个模型中都如下所示:

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

希望对你有帮助...

【讨论】:

【参考方案4】:

上述答案虽然很好,但不会在数据库中创建外键约束,而只是创建索引和 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 以及您正在使用的数据库中的外键约束上创建索引。

【讨论】:

这是 rails 5 的正确解决方案

以上是关于Ruby on rails - 两次引用同一个模型?的主要内容,如果未能解决你的问题,请参考以下文章

Ruby on Rails 复数模型名称

ruby on rails (模型关联查询)

在 Rails 中用一个简单的实体两次引用相同的模型?

Ruby on Rails Tutorial 第二章

Ruby on Rails 生成模型

ruby on rails 6 - 在javascript中动态引用图像