如何在Rails中建立相互友谊的模型

我知道之前在Stack Overflow上已经提出了这个问题,但答案并没有以我能解释的方式为我做。 我的一般方法受本教程的启发。

我正在尝试做的是为友好用户创建一个非常简单的模型,通过单个记录在两端创建相同的友谊。

在db级别,我只有一个’friendships’表,它只有一个user_id,一个friend_id和一个is_pending布尔列。

在user.rb中,我将关系定义为:

has_many :friendships has_many :friends, through: :friendships 

在friendship.rb中,我将关系定义为:

 belongs_to :user belongs_to :friend, :class_name => 'User' 

如果我添加友谊,我可以访问如下:

 > a = User.first > b = User.last > Friendship.new(a.id, b.id) > a.friends => # 

这是完美的,但我想要的是也能够像其他方向一样:

 > b.friends 

不幸的是,随着关系的定义,我得到一个空集合。 运行的SQL显示它正在搜索user_id = b.id. 如何指定它还应搜索friend_id = b.id?

也许这个:

 friendship.rb belongs_to :friend_one, :foreign_key => :user_id belongs_to :friend_two, :foreign_key => :friendship_id 

 user.rb has_many :friendship_ones, :class_name => 'Friendship', :foreign_key => :friendship_id has_many :friend_ones, through: :friendship_ones has_many :friendship_twos, :class_name => 'Friendship', :foreign_key => :user_id has_many :friend_twos, through: :friendship_twos def friends friend_ones + friend_twos end 

您可以通过两个查询来查找朋友,但这是一个简单的数据模型,您只需调用@ user.friends即可查找实例。

如果你加载两个friend_ones和friend_twos关联,它将适合加载。

本文介绍如何建立互惠关系: Rails中的双向关系

它显示了如何使用after_createafter_destroy来插入模拟互惠关系的其他关系。 通过这种方式,您可以在连接表中获得双倍的记录,但是您可以灵活地使用a.friendsb.friends并且看到两者都正确地相互包含。

使其适用于您的模型:

 class Person < ActiveRecord::Base has_many :friendships, :dependent => :destroy has_many :friends, :through => :friendships, :source => :person end class Friendship < ActiveRecord::Base belongs_to :person, :foreign_key => :friend_id after_create do |p| if !Friendship.find(:first, :conditions => { :friend_id => p.person_id }) Friendship.create!(:person_id => p.friend_id, :friend_id => p.person_id) end end after_update do |p| reciprocal = Friendship.find(:first, :conditions => { :friend_id => p.person_id }) reciprocal.is_pending = self.is_pending unless reciprocal.nil? end after_destroy do |p| reciprocal = Friendship.find(:first, :conditions => { :friend_id => p.person_id }) reciprocal.destroy unless reciprocal.nil? end end 

我已经在一些项目中成功使用了这种方法,而且方便性非常棒!