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 表需要同时跟踪 sender
和 recipient
。
显然,对于单一的关系,我会这样做:
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:references
和 recipient: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_id
和 recipient_id
以及您正在使用的数据库中的外键约束上创建索引。
【讨论】:
这是 rails 5 的正确解决方案以上是关于Ruby on rails - 两次引用同一个模型?的主要内容,如果未能解决你的问题,请参考以下文章