【问题标题】:Bi-Directional has_and_belongs_to_many on a Single Model in RailsRails 中单个模型上的双向 has_and_belongs_to_many
【发布时间】:2017-05-10 01:25:17
【问题描述】:

我有用户模型,我想在用户之间建立友谊。我已经使用 has_and_belongs_to_many 实现了这一目标

has_and_belongs_to_many :friends, class_name: 'User', foreign_key: :friend_id

我有这个关联的表:

create_table :users_users do |t|
  t.uuid :user_id, null: false
  t.uuid :friend_id, null: false
  t.timestamps
end

当我通过 user.friends 结交朋友时,我需要关联当前用户在 user_id 列或friend_id 中的位置。我当前的解决方案只返回用户在friend_id 表中的记录。

我有两个解决方案:

  1. 重复记录 [{user_id: 1, friend_id: 2}, {user_id: 2,friend_id: 1}]
  2. 覆盖 user.friends 方法并返回我的自定义查询

这两种解决方案都不是优雅和最优的。

我已经阅读了article 的相关信息,但是这篇文章的解决方案非常复杂。

这个问题还有其他解决方案吗?我正在使用 Rails 5。

【问题讨论】:

    标签: ruby-on-rails activerecord associations has-and-belongs-to-many has-many


    【解决方案1】:

    首先,我宁愿使用那篇文章解决方案或一个查询来获取 所有记录,而不是这个。

    执行此操作的活动记录方式是:

    1. 创建has_many 关系而不是HABTM 关系
    2. 查询users_users表以查找匹配user_id=my_user.idfriend_id=my_user.id的所有记录
    3. 展平结果
    4. 独一无二
    5. 从数组中删除当前用户

    所以我建议把这个方法放在用户模型中:

    def all_friends
        UserFriend.includes(:user, :friend).where("user_id=? OR friend_id=?", id , id).map {|rel| [rel.user, rel.friend] }.flatten.uniq{|u| u.id}.reject{|u| u.id == self.id}
    end
    

    UserFriend模特:

    class CreateUserFriends < ActiveRecord::Migration[5.0]
      def change
        create_table :user_friends do |t|
          t.belongs_to :user, foreign_key: true
          t.belongs_to :friend, foreign_key: 'friend_id', class: 'User'
    
          t.timestamps
        end
      end
    end
    

    【讨论】:

    • 与关联和作用域相比,方法的缺点是在某些情况下,当使用merge 和相关机制时,它们更难重复使用。
    • @ma_il 同意。我不喜欢这种技术,但它确实有效。
    【解决方案2】:

    不是 100% 确定这会起作用,但仍然想在我得到一些急需的睡眠之前把它给你尝试一下;) 为您的关联指定自定义查询方法:

    has_and_belongs_to_many :friends, -> {
      where("friend_id = :user_id OR user_id = :user_id", user_id: id)
    }, class_name: 'User', foreign_key: friend_id
    

    【讨论】:

    • 这不起作用,因为 Rails 使用内部连接来获取这些记录,并且该查询将与原始查询合并。现在查询如下所示:SELECT COUNT(*) FROM "users" INNER JOIN "users_users" ON "users"."id" = "users_users"."user_id" WHERE "users_users"."friend_id" = $1 AND (friend_id = '96b232eb-43ea-4eaa-9f47-9eae0516fde2' OR user_id = '96b232eb-43ea-4eaa-9f47-9eae0516fde2') [["friend_id", "96b232eb-43ea-4eaa-9f47-9eae0516fde2"]]
    • 这仍然可以通过将连接表名称显式添加到查询中来解决。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多