【问题标题】:Model design: Users have friends which are users模型设计:用户有朋友是用户
【发布时间】:2016-03-16 11:16:23
【问题描述】:

我希望在寻求这种关联之前确保我的方法是正确的。实现听起来太复杂了,所以我认为我的计划一定有问题。我正在使用符合 Rails 存储约定的结构化 (SQL) 数据存储。我拥有的是一个用户模型,它有一个电子邮件地址password_digest,并在架构中具有名称。

class User < ActiveRecord::Base
  has_many :posts
end

我想实现一个has_many 关联到朋友集合,以便用户可以belong_to 用户(作为朋友)。我希望能够让User.last.friends.last 在正确构建和填充时返回一个用户对象。

我相信我可以为这种关联创建一个模型,例如:

Class Friend < ActiveRecord::Base
  belongs_to :user
  belongs_to :friendlies, class: 'User'
end
Class User < ActiveRecord::Base
  has_many :posts
  has_many :friends
  has_many :friendly, class: 'Friend'
end

但我认为这将需要我添加模型并使用User.last.friends.last.user 进行查询所以我在想这是has_and_belongs_to_many 关系。我可以摆脱以下(或类似的):

class User < ActiveRecord::Base
  has_and_belongs_to_many :friends, class: 'User'
end

我找到this

class User < ActiveRecord::Base
  has_many :user_friendships
  has_many :friends, through: :user_friendships


class UserFriendship < ActiveRecord::Base
  belongs_to :user
  belongs_to :friend, class_name: 'User', foreign_key: 'friend_id'

还有this(自称“标准”):

has_many :friendships, :dependent => :destroy
has_many :friends, :through => :friendships, :dependent => :destroy
has_many :inverse_friendships, :class_name => "Friendship", :foreign_key => "friend_id", :dependent => :destroy
has_many :inverse_friends, :through => :inverse_friendships, :source => :user, :dependent => :destroy

我认为这需要Friendship 模型。我不觉得我需要一个友谊模型。我认为class UserFriendship 方法看起来不错,但它需要一个额外的模型。现在进入问题:

  1. 我能否在不产生额外模型的情况下与将用户与作为用户的朋友相关联的表建立has_and_belongs_to_many 关系?

  2. “以防万一”以后出现其他要求,是否谨慎使用附加模型?

【问题讨论】:

    标签: ruby-on-rails data-modeling


    【解决方案1】:

    您要查找的是称为self referential join 的东西,这意味着您可以使用不同的关联引用相同的model

    #app/models/user.rb
    class User < ActiveRecord::Base
       has_and_belongs_to_many :friendships,
          class_name: "User", 
          join_table:  :friendships, 
          foreign_key: :user_id, 
          association_foreign_key: :friend_user_id
    end
    

    您必须创建一个 habtm 连接表才能获得参考:

    $ rails g migration CreateFriendshipsTable
    
    #db/migrate/create_friendships_table____.rb
    class CreateFriendShipsTable < ActiveRecord::Migration
       def change
          create_table :friendships, id: false do |t|
            t.integer :user_id
            t.integer :friend_user_id
          end
    
          add_index(:friendships, [:user_id, :friend_user_id], :unique => true)
          add_index(:friendships, [:friend_user_id, :user_id], :unique => true)
       end
    end
    
    $ rake db:migrate
    

    这里有一个很好的参考:Rails: self join scheme with has_and_belongs_to_many?


    增加一个模型是否谨慎'以防以后出现'额外要求?

    只有当你知道你才会有额外的属性。如果没有,您可以随时使用habtm

    【讨论】:

    • 为什么 id: false?
    【解决方案2】:

    Rich 的回答是一个极好的资源。通过遵循这个约定,我设法颠覆了几个步骤:

    # app/model/user.rb
    has_and_belongs_to_many :friends,
      class_name: "User", 
      join_table: :friends_users, 
      foreign_key: :user_id, 
      association_foreign_key: :friend_id
    

    这个迁移就足够了(无需修改生成的代码):

    rails g migration CreateFriendlyJoinTable users friends
    

    JoinTable 语法是使用 rails generate migration -h 找到的

    【讨论】:

      【解决方案3】:

      你可以试试这个

      --friendship.rb

      class Friendship < ApplicationRecord
        belongs_to :user
        belongs_to :friend, :class_name => 'User'
      end
      

      --user.rb

      class User < ApplicationRecord
        has_many :friendships
        has_many :friends, through: :friendships
      end
      

      --db migrate xxxx_create_friendship.rb

      class CreateFriendships < ActiveRecord::Migration[5.0]
        def change
          create_table :friendships do |t|
            t.belongs_to :user
            t.belongs_to :friend, class: 'User'
            t.timestamps
          end
        end
      end
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-02-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多