我有一张友谊的桌子。它的构建方式与http://railscasts.com/episodes/163-self-referential-association类似。现在我希望以最有效的方式获得所有关系,这些关系是双方的(正常和反向友谊)。
在MySQL中它应该看起来像:
SELECT * FROM Friendship as f1, Friendship as f2 WHERE f1.user_id = f2.friend_id AND f1.friend_id = f2.user_id
在对railscast的评论中,有人构建了mutual_friends部分的扩展。但这三条线在我的案例中不起作用:
设计用户模型:
class User < ActiveRecord::Base
has_many :features
has_and_belongs_to_many :tags
has_many :friendships
has_many :friends, through: :friendships
has_many :inverse_friendships, class_name: 'Friendship', foreign_key: 'friend_id'
has_many :inverse_friends, through: :inverse_friendships, source: :user
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :first_name, :last_name, :email, :password, :date_of_birth, :gender, :password_confirmation
validates_inclusion_of :gender, :in => ['female', 'male'], message: 'Kein Geschlecht ausgewaehlt'
validates :first_name, presence: true
validates :last_name, presence: true
validates_date :date_of_birth, :on_or_before => lambda { Date.current }
def mutual_friends
inverse_friends.where('user_id in (?)', friend_user_ids)
end
end
设计用户迁移:
class DeviseCreateUsers < ActiveRecord::Migration
def change
create_table(:users) do |t|
t.string :email, :null => false, :default => ""
t.string :encrypted_password, :null => false, :default => ""
t.string :reset_password_token
t.datetime :reset_password_sent_at
t.datetime :remember_created_at
t.integer :sign_in_count, :default => 0
t.datetime :current_sign_in_at
t.datetime :last_sign_in_at
t.string :current_sign_in_ip
t.string :last_sign_in_ip
t.string :first_name
t.string :last_name
t.datetime :date_of_birth
t.string :gender
t.integer :tag_id
t.timestamps
end
add_index :users, :email, :unique => true
add_index :users, :reset_password_token, :unique => true
end
end
友谊模式
class Friendship < ActiveRecord::Base
attr_accessible :user_id, :friend_id
belongs_to :user
belongs_to :friend, class_name: 'User'
end
友谊迁移
class CreateFriendships < ActiveRecord::Migration
def self.up
create_table :friendships do |t|
t.integer :user_id
t.integer :friend_id
t.timestamps
end
end
def self.down
drop_table :friendships
end
end
此解决方案中的错误是:
undefined local variable or method `friend_user_ids' for #<User:0x007fcb93e86de8>
Railscast评论中的原始星座是:
朋友模特
class Friend < ActiveRecord::Base
belongs_to :profile
belongs_to :friend, :class_name => 'Profile'
end
个人资料模型
class Profile < ActiveRecord::Base
has_many :friends
has_many :friend_profiles, :through => :friends, source: :friend
has_many :friended_by, class_name: "Friend" , :foreign_key => "friend_id"
has_many :friended_by_profiles, :through => :friended_by, source: :profile
def mutual_friends
friended_by.where('profile_id in (?)', friend_profile_ids)
end
def mutual_friend_profiles
Profile.where('id in (?)', mutual_friends.pluck(:profile_id))
end
def requesting_friends
friended_by.where('profile_id not in (?)', friend_profile_ids)
end
def requesting_friend_profiles
Profile.where('id in (?)', requesting_friends.pluck(:profile_id))
end
def pending_friends
friends.where('friend_id not in (?)', friended_by_profile_ids)
end
def pending_friend_profiles
Profile.where('id in (?)', pending_friends.pluck(:friend_id))
end
end
感谢您的帮助!
答案 0 :(得分:0)
我如何让它工作的解决方案: 删除共同的朋友
def mutual_friends
inverse_friends.where('user_id in (?)', friend_user_ids)
end
将友谊控制器中的逻辑更改为:
#all mutual friendships with marked true in both relations
@user = current_user
@user_friends = @user.friendships.select{ |friendship| friendship.marked == true }
@user_inverse_friends = @user.inverse_friendships.select{ |friendship| friendship.marked == true }
@friendships = @user_friends.select{ |friend| @user_inverse_friends.map{|inverse_friend| inverse_friend.user_id}.include? friend.friend_id}
#friendships with false mark
@skiped_friendships = current_user.friendships.select { |friendship| friendship.marked != true}
#marked friend: users friendships without the mutual valid friendships
@marked_friends = current_user.friendships - @friendships - @skiped_friendships