【问题标题】:has_and_belongs_to_many self join only associates one wayhas_and_belongs_to_many 自加入只关联一种方式
【发布时间】:2014-06-22 03:19:40
【问题描述】:

我有一个用户模型,我想通过使用 has_and_belongs_to_many 的自连接连接到它自己。我让它几乎按预期工作,除了我希望它以两种方式关联两个用户。

我的用户类:

class User < ActiveRecord::Base
  ...
  has_and_belongs_to_many :friends,
      autosave: true,
      class_name: 'User',
      join_table: :friendships,
      foreign_key: :user_id,
      association_foreign_key: :friend_user_id
  ...
end

我的迁移:

class CreateFriendships < ActiveRecord::Migration
  def self.up
    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

  def self.down
    remove_index(:friendships, [:friend_user_id, :user_id])
    remove_index(:friendships, [:user_id, :friend_user_id])
    drop_table :friendships
  end
end

我的问题:

user1 = User.find(1)
user2 = User.find(2)

user1.friends << user2
user1.reload.friends.exists?(user2) # true
user2.reload.friends.exists?(user1) # false <- My problem

我怎样才能让这种关系双向运作?由于在这种情况下友谊总是相互的,而关于 SO 的其他问题看起来应该是可能的,我希望最后两个陈述都返回 true。

【问题讨论】:

  • 您也可以查看the solution我在 SO 上发布的类似问题。

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


【解决方案1】:

不那么老套的方法是在你自己的方法中建立友谊:

class User < ActiveRecord::Base
  def make_friend(user)
    # TODO: put in check that association does not exist
    self.friends << user
    user.friends << self
  end
end

然后这样称呼它

user1.make_friend(user2)
# should set both friends know about each other

更骇人听闻的是用ActiveRecord::Associations 方法覆盖来欺骗。例如。 has_and_belongs_to_many's 方法 collection&lt;&lt;(object, …) 可以针对您的情况进行修改,如下所示:

class User < ActiveRecord::Base
  attr_accessor :reversed  # we use it to avoid stack level too deep issue
  has_and_belongs_to_many :friends, ... do
    def << (new_friend)
      reversed = true
      # it should not trigger on our friend record as self.reversed is true
      new_friend.friends << self unless new_friend.reversed
      super new_friend
    end     
  end
end

注意:我不确定self&lt;&lt; 方法中的含义,所以您可能应该通过关系对象以某种方式挖掘真实的对象实例。

【讨论】:

  • 你想出了与我在此期间几乎相同的解决方案,所以我可能会使用它。 def add_friend(user) self.friends &lt;&lt; user unless self.friends.exists?(user) user.friends &lt;&lt; self unless user.friends.exists?(self) end
【解决方案2】:

你也可以...

user2.friends << user1

确实意味着有两条连接记录。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2021-05-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多