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
And these are the models
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", :fore开发者_如何学Pythonign_key => "friend_id"
has_many :likes, :foreign_key => :user_id,
end
class Like < ActiveRecord::Base
belongs_to :user
belongs_to :friendship
end
I'm trying to get "likes of friends" but i can't.
"User.find(1).friends_likes" gives that sql query
SELECT "likes".* FROM "likes" INNER JOIN "friendships" ON "likes".user_id = "friendships".id WHERE (("friendships".user_id = 1))
but i think that has to be "INNER JOIN "friendships" ON "likes".user_id = "friendships".friend_id"
how can i do that? thanks
The simplest solution is probably to add an instance method friends_likes
on the User
model that constructs the correct SQL:
def likes_of_friends
friends.includes(:likes).map(&:likes)
end
The .includes(:likes)
is for performance, to avoid an N+1 query situation.
Then your User.find(1).friends_likes
will produce the following queries, assuming user 1 has friends with ids 2 and 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))
If you really need everything in one query, you could write straight-up 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;
")
The reason it isn't more straightforward is because one User "owns" the friendship - it's one-way, and there doesn't seem to be a way to get the Friendships associated with the "Friend" (specified by friend_id
on the friendships table).
So, adding that opposite direction will help (weird naming aside):
class Friendships
# ...
has_many :befriendedships, :class_name => "Friendship", :foreign_key => "friend_id"
end
Then, you can query for the thing you're looking for a bit more simply:
Like.joins(:user => :befriendedships).where(["friendships.user_id = ?", 1])
That'll generate essentially the same SQL as the find_by_sql
example.
精彩评论