如何列出由多对多关系实现的具有收藏夹状态的所有帖子



我有三个型号UserPostFavorite

class User < ApplicationRecord
has_many :posts
end
class Post < ApplicationRecord
belongs_to :user
end
class Favorite < ApplicationRecord
belongs_to :post
belongs_to :user
end
> user = User.create(name: 'user1')
> user.posts.create(title: 'Title 1')
> user.posts.create(title: 'Title 2')
> Favorite.create(user_id: 1, post_id: 2)

我想检索所有属于user1的帖子,并由用户保存最喜欢的状态。

> User.first.posts_with_fav_status
=>  [#<Post id: 1, title: "Title 1", user_id: 1, faved: false> ],
[#<Post id: 2, title: "Title 2", user_id: 1, faved: true> ]

我该如何编写这样的查询方法?

编辑

我可以通过以下查询获得最喜欢的状态。但是这个查询每次都调用子查询。当数据库变得更大时,速度会太慢。如何重写此查询?

def posts_with_fav_status
posts.select(<<-SQL)
*, 
EXISTS(SELECT * FROM favorites 
WHERE favorites.user_id = posts.user_id
AND favorites.post_id = posts.id) as faved
SQL
end

我找到了这个解决方案(在Rails 6.1.4.4上测试(

class User < ApplicationRecord
has_many :posts
# User.first.posts_with_faved
def posts_with_faved
posts.select("posts.*, favorites.user_id = #{id} as faved")
.left_joins(:favorites)
end
end
# due to how the inspect works, you won't see the faved attribute in the output, but it is there
irb(main):053:0> first, second = User.first.posts_with_faved
User Load (0.2ms)  SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
Post Load (0.2ms)  SELECT posts.*, favorites.user_id = 1 as faved FROM "posts" LEFT OUTER JOIN "favorites" ON "favorites"."post_id" = "posts"."id" WHERE "posts"."user_id" = ?  [["user_id", 1]]
=> #<ActiveRecord::AssociationRelation [#<Post id: 1, title: "Title 1", user_id: 1, created_at: "2022-01-28 09:34:19.048598000 +0000", updated_at: "2022-01-28 09:34:19.048598000 +0000">, #<Post id: 2, title: "Title 2", user_id: 1, created_at: "2022-01-28 09:34:22.172245000 +0000", ...
irb(main):054:0> first.faved
=> nil
irb(main):055:0> second.faved
=> 1

需要考虑的事项:

  • 如果链接查询,请小心不要用select重写。否则一切都会崩溃
  • 查询不返回布尔值,但几乎返回:nil而不是false1而不是true。如果您将其与if一起使用,这仍然有效

您错过了UserPost模型中的关系:

用户:

class User < ApplicationRecord
has_many :posts
has_many :favorites
has_many :favorite_posts, through: :favorites, source: :post
end

职位:

class Post < ApplicationRecord
belongs_to :user
has_many :favorites
has_many :favorited_by, through: :favorites, source: :user
end

用它你可以写

u = User.first
u.favorite_posts.where(user_id: u.id)

您拥有表和模型的方式我看不出有什么方法可以避免子查询来获得您想要的结果。

不确定你的用例是什么,但我假设这些是用户正在保存的帖子,有些将是收藏夹,因为如果用户是帖子的作者,并且他们喜欢自己的帖子,那么为什么不在posts表中添加一列,将其指定为收藏夹呢?

如果我的假设是正确的,那么放弃Favorite模型,转而使用包括user_idpost_idfavoriteUserPostsloookup模型可能会更好。

Class User < ApplicationRecord
has_many :user_posts
has_many :posts, through: :user_posts
end
Class UserPost < ApplicationRecord
belongs_to :user
belongs_to :post
end
Class Post < ApplicationRecord
has_many :user_posts
has_many :users, through: :user_posts
end

然后你可以使用

user = User.first
user_posts = user.user_posts.eager_load(:posts)

faveuser_posts表/模型上,因此它可以由user_posts.first.fave?和实际发布内容user_posts.first.post访问。

您可以将accepts_nested_attributes_for :post添加到UserPost模型中,并为用户创建新的帖子关联:

User.first.user_posts.create(fave: true, post: {title: 'title text', ...})

除此之外,您还必须使用子查询或第二个查询,然后重新映射到您自己设计的哈希/数组中。

最新更新