假设我有一个帖子的经典示例,它通过标签连接表有很多标签。我如何将特定标签一次插入到满足特定条件的多个帖子中。
在伪代码中,我想我首先过滤掉任何已经具有所需标签的帖子。
tag_id = Tag.first # the desired tag
posts = Post.first(50) # example query of subset of posts
posts = posts.joins(:tags).where.not(tags: { id: tag_id }) # filter out posts that already have desired tag
现在我需要将所有选定的帖子 ID 和所需的标签 ID 插入到标签表中。
我想知道是否有一种“rails”方式可以做到这一点?
感谢您的帮助!
鉴于:
tag = Tag.first # the desired tag. Note I changed to getting the object and calling the .id method later as your code was incorrect.
posts = Post.first(50) # example query of subset of posts
filtered_posts = posts.joins(:tags).where.not(tags: { id: tag.id }) # filter out posts that already have desired tag
一种方法:
filtered_posts.each do |p|
p.tags << tag
end
这会循环浏览帖子并为您创建加入记录。我想说这非常 Rails-y,因为它使用内置关联,因此您甚至不需要提及标记连接表。另外,我假设您有一些验证,其中一个帖子不能用相同的标签标记两次。这可以防止这种情况发生。如果由于某种原因您需要向数千个帖子添加标签(似乎不太可能,但您永远不知道),有一些方法可以绕过为每个帖子构建对象并使用 SQL 进行更改。如果您想要运行最少的查询,您可以这样做:
t_id = tag.id
#=> 33
post_ids = filtered_posts.ids # .ids is a built in method to get all ids without a new query
#=> [1, 23, 45, 94, ....]
insert_hash = post_ids.map {|p_id| {post_id: p_id, tag_id: t_id} }
#=> [{post_id: 1, tag_id: 33}, {post_id: 23, tag_id: 33}, {post_id: 45, tag_id: 33}, ...]
Tagging.insert_all(insert_hash)
#=> Tagging Bulk Insert (8.3ms) INSERT INTO "taggings" ("post_id","tag_id","created_at","updated_at") VALUES (1, 33, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), (23, 33, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)...
如果您使用它,请参考
insert_all
的文档,因为它会绕过正常 Rails 流程中的验证等。 https://api.rubyonrails.org/classes/ActiveRecord/Persistence/ClassMethods.html#method-i-insert_all