:has_many:通过没有约定名称的关联?

时间:2011-04-03 15:58:31

标签: ruby-on-rails has-many-through

create_table "friendships", :force => true do |t|
    t.integer  "user_id"
    t.integer  "friend_id"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "likes", :force => true do |t|
    t.string   "name"
    t.integer  "user_id"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "users", :force => true do |t|
    t.string   "name"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

这些是模型

class User < ActiveRecord::Base

    has_many :friendships
    has_many :friends, :through => :friendships
    has_many :likes
    has_many :friends_likes, :through => :friendships, :source => :likes

end

class Friendship < ActiveRecord::Base

    belongs_to :user
    belongs_to :friend, :class_name => "User", :foreign_key => "friend_id"
    has_many :likes, :foreign_key => :user_id, 
end

class Like < ActiveRecord::Base

    belongs_to :user
    belongs_to :friendship

end

我想要“喜欢朋友”,但我不能。

“User.find(1).friends_likes”给出了sql查询

SELECT "likes".* FROM "likes" INNER JOIN "friendships" ON "likes".user_id = "friendships".id WHERE (("friendships".user_id = 1))

但我认为必须是“INNER JOIN”友谊“ON”喜欢“.user_id =”friendships“。 friend_id

我该怎么做? 感谢

1 个答案:

答案 0 :(得分:1)

最简单的解决方案可能是在构造正确SQL的friends_likes模型上添加实例方法User

  def likes_of_friends
    friends.includes(:likes).map(&:likes)
  end

.includes(:likes)用于提高性能,以避免出现N + 1查询情况。

然后您的User.find(1).friends_likes会产生以下查询,假设用户1有ids 2和3的朋友:

  User Load (0.1ms)  SELECT `users`.* FROM `users` LIMIT 1
  User Load (0.4ms)  SELECT `users`.* FROM `users` INNER JOIN `friendships` ON `users`.id = `friendships`.friend_id WHERE ((`friendships`.user_id = 1))
  Like Load (0.2ms)  SELECT `likes`.* FROM `likes` WHERE (`likes`.user_id IN (2,3))

如果你真的需要一个查询中的所有内容,你可以编写直接的SQL:

Like.find_by_sql("select likes.* from
  likes
    inner join users as friends
      on friends.id = likes.user_id
    inner join friendships
      on friendships.friend_id = friends.id
  where friendships.users_id = 1;
")

它不是更直接的原因是因为一个用户“拥有”友谊 - 它是单向的,并且似乎没有办法让友谊与“朋友”相关联(由{指定)在友谊表上{1}}。

所以,添加相反的方向将有所帮助(奇怪的命名除外):

friend_id

然后,您可以更简单地查询您要查找的内容:

class Friendships
  # ...
  has_many :befriendedships, :class_name => "Friendship", :foreign_key => "friend_id"
end

这将生成与Like.joins(:user => :befriendedships).where(["friendships.user_id = ?", 1]) 示例基本相同的SQL。