1

这应该很容易解决,但我是 Rails 新手。现在深入研究有关关联的 Rails 指南,但不断遇到障碍。

我有一个可以向其他用户发送消息的用户模型。为此,所有线程都有一个对话模型,每个单独的消息都有一个对话项模型。

这些是现在的协会:

用户.rb

has_many :received_messages, as: :recipient, class_name: 'Conversation', foreign_key: :recipient_id
has_many :sent_messages, as: :sender, class_name: 'Conversation', foreign_key: :sender_id
has_many :received_messages, as: :recipient, class_name: 'Conversation_item', foreign_key: :recipient_id
has_many :sent_messages, as: :sender, class_name: 'Conversation_item', foreign_key: :sender_id

对话.rb

has_many :conversation_items, dependent: :destroy
belongs_to :sender, polymorphic: true
belongs_to :recipient, polymorphic: true

对话项目.rb

belongs_to :conversation
belongs_to :sender, polymorphic: true
belongs_to :recipient, polymorphic: true

我正在尝试通过他的对话项目访问用户,如下所示:

<%= conversation_item.sender.inspect %>

这返回nil- 属性 sender 似乎确实有效,但不知何故不包含任何内容。但是,对话项目确实有一个 sender_id。

我在上面的关联中缺少什么?还是其他地方?

4

2 回答 2

2

在我看来,您不需要在这里使用多态关系。你应该能够做到这一点:

class User < ActiveRecord::Base
  has_many :participations, class_name: 'Participant'
  has_many :conversations, through: :participations
  has_many :conversation_items, through: :conversations
end

class Participant < ActiveRecord::Base
  belongs_to :user
  belongs_to :conversation
end 

class Conversation < ActiveRecord::Base
  has_many :participants
  has_many :conversation_items
end

class ConversationItem < ActiveRecord::Base
  belongs_to :conversation
  belongs_to :sender, class_name: 'User', foreign_key: 'sender_id'
end

对我来说,这作为一个领域模型更有意义,其中一个对话有很多参与者。您可以将发件人附加到对话项目,并且对话中的每个参与者(不是发件人)都是隐含的收件人。对话有发送者和接收者是没有意义的,因为对话是双向或多向对话。

多态关系在这里不起作用的原因是多态关系适用于表可以引用不同类型项目的情况。例如,假设您的 ConversationItem 有很多附件,并且附件也可以应用于帖子。你会有类似的东西:

class ConversationItem < ActiveRecord::Base
  #...
  has_many :attachments, as: :attachable
  #...
end

class Post < ActiveRecord::Base
  has_many :attachments, as: :attachable
end

class Attachment < ActiveRecord::Base
  belongs_to :attachable, polymorphic: true
end

附件将具有字段attachable_idattachable_type. 当您从对话项目中查询附件时,您将有一个类似的查询SELECT * FROM attachments where attachable_id = <id of the conversation item> and attachable_type = 'ConversationItem'。因此,多态关联使您能够将模型附加到许多不同类型的其他模型。

在这种情况下,您可以看到拥有多态关系是如何没有意义的,除非您将拥有许多不同类型的模型来发送和接收对话项目,因为在您的 conversation_items 表中,您将拥有sender_type, sender_id, recipient_type, recipient_id

您的模型遇到的另一个问题是您尝试使用不同的参数两次定义相同的关系。当您调用 时has_many :foo,Rails 会生成一堆不同的方法,例如foosfoos=等等。如果您再次调用它,您只是使用新参数重新定义这些方法。

跟进

您不一定需要有 Participant 模型。拥有它使您能够让多个用户参与,并且可以避免为同一模型创建多个关系。您可以通过关系实现相同的目的has_and_belongs_to_many,但是如果需要,您将失去将额外属性附加到连接表的能力。

例如,您希望允许人们退出对话而不删除他们曾经是参与者的信息,使用加入模型,您可以添加一个布尔字段调用active,设置为false某人退出的时间。使用 HABTM,您无法做到这一点。让参与者离开需要从连接表中完全删除该对。我通常更喜欢加入模型,比如这里的参与模型,因为你永远不知道你的模式会如何演变。

也就是说,这里是 HABTM 的一个例子:

class User < ActiveRecord::Base
  has_and_belongs_to_many :conversations
end

class Conversation < ActiveRecord::Base
  has_and_belongs_to_many :users
end

在这里,您没有连接模型,但您需要创建一个连接表才能使其工作。一个迁移的例子是:

create_table :conversations_users, :id => false do |t|
  t.integer :conversation_id
  t.integer :user_id
end

add_index :conversations_users, [:conversation_id, :user_id], unique: true

连接表的组成部分的名称按字母顺序排列,即conversations_users不是users_conversations

进一步跟进

如果您使用 Participant 表,外键将为 onparticipantsconversation_items

participants
  user_id
  conversation_id

conversation_items
  conversation_id
  sender_id
于 2012-12-08T20:24:03.510 回答
-1

你模型的逻辑似乎很好。我的建议是控制器和视图结构。当您在 Rails 中创建关联时,嵌套资源的常用方法(在您的 routes.rb 中):

资源 :conversations 做资源 :conversation_items 结束

然后你的 Conversation_items 控制器应该在 new 和 create actions 中有这样的东西:

def new
  @conversation = Conversation.find(params[:conversation_id]
  @conversation_item = Conversation_item.new
end

def create
  @conversation = Conversation.find(params[:conversation_id]
  @conversation_item = @conversation.conversation_items.build(params[:conversation_items]
end

最后,在您的 _form.html.erb (同时提供创建和更新操作)中:

  <%= form_for [@conversation, @conversation_item] do |f| %>

     # your code here

  <%= end %>

我在那里,正如你在这里看到的那样......它是苦乐参半的:)

于 2012-12-08T18:47:48.210 回答