【问题标题】:Ruby on rails - Reference the same model twice?Ruby on rails - 两次引用同一个模型?
【发布时间】: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

任何建议将不胜感激。

谢谢。

【问题讨论】:

    标签: ruby-on-rails rails-activerecord relationship


    【解决方案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 的正确解决方案
        猜你喜欢
        • 2012-09-20
        • 1970-01-01
        • 2021-08-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-06-12
        • 2014-10-20
        • 1970-01-01
        相关资源
        最近更新 更多