:has_many :通过关联而无需约定名称?

发布于 2024-10-29 12:41:35 字数 1384 浏览 0 评论 0原文

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“friendships”ON“likes”.user_id =“friendships”。friend_id

我怎么能这样做吗? 谢谢

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", :foreign_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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

花开半夏魅人心 2024-11-05 12:41:35

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

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

.includes(:likes) 是为了性能,避免N+1查询的情况。

那么您的 User.find(1).friends_likes 将生成以下查询,假设用户 1 有 ID 为 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;
")

它不是更简单的原因是因为一个用户“拥有”友谊 - 这是单向的,并且似乎没有一种方法可以获得与“朋友”关联的友谊(由 指定)好友表上的friend_id)。

因此,添加相反的方向会有所帮助(除了奇怪的命名):

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

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

Like.joins(:user => :befriendedships).where(["friendships.user_id = ?", 1])

这将生成与 find_by_sql 基本相同的 SQL例子。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文