ruby-on-rails - 将 where() 与 join() 和 includes() 一起使用时出现 ActiveRecord 预加载错误
问题描述
我想查找具有特定评论的帖子并打印出所有评论(不仅仅是那个特定评论)。
为了搜索关联模型,我使用了 where() 和 joins() ,如下代码:
posts = Post.joins(:comments).where('comments.value = "value1"')
这会返回正确的帖子,但是当我尝试使用结果中的评论时会生成 n + 1 个查询。所以我尝试使用includes()来预加载:
posts = Post.includes(:comments).joins(:comments).where('comments.value = "value1"')
这也会返回正确的帖子,但是当我尝试调用时post.comments
,它在每个帖子中只包含一条评论(值为“value1”的评论),其中每条帖子都应该有多个评论。
对我来说,这似乎是一个 Rails 错误,post.comments
应该总是返回所有相关的注释,但在这种情况下它只返回一个。
我的问题是:
- 这是 Rails 错误还是我误解了 ActiveRecord 应该如何工作?
- 查找所有带有此类评论的帖子并打印所有帖子评论而不生成 + 1 查询的最佳方法是什么?
代码重现错误:
# frozen_string_literal: true
begin
require "bundler/inline"
rescue LoadError => e
$stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
raise e
end
gemfile(true) do
source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
# Activate the gem you are reporting the issue against.
gem "activerecord", "5.2.0"
gem "sqlite3"
end
require "active_record"
require "minitest/autorun"
require "logger"
require "pp"
# Ensure backward compatibility with Minitest 4
Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)
# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Schema.define do
create_table :posts, force: true do |t|
end
create_table :comments, force: true do |t|
t.string :value
t.integer :post_id
end
end
class Post < ActiveRecord::Base
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :post
end
class BugTest < Minitest::Test
def test_association_stuff
post = Post.create!
post.comments << Comment.create(value: 'value1')
post.comments << Comment.create(value: 'value2')
post.comments << Comment.create(value: 'value3')
post = Post.create!
post.comments << Comment.create(value: 'value1')
post.comments << Comment.create(value: 'value2')
post.comments << Comment.create(value: 'value3')
# find the post that has comment 'value1'
posts = Post.joins(:comments).where('comments.value LIKE "%value1%"')
posts.each { |p| pp p.comments }
assert_equal 3, posts.first.comments.length
# to pervent n + 1, use includes as well
posts = Post.includes(:comments).joins(:comments).where('comments.value LIKE "%value1%"')
posts.each { |p| pp p.comments }
assert_equal 3, posts.first.comments.length
end
end
解决方案
推荐阅读
- amazon-web-services - AWS::S3::NoSuchKey 指定的键不存在
- swift - 来自 AudioKit 的 sysex 接收的 MIDISystemCommand
- excel - 如果 1 列或两列都有单词,则输入文本
- javascript - @babel/register 中忽略的正确正则表达式模式是什么
- javascript - 在 next.js 中做路由的最佳方法是什么
- mysql - MySQL:从组描述中重新查找用户组
- node.js - 强制从私有注册表下载 npm
- ruby-on-rails - Rails API ActiveStorage:获取公共 URL 以显示来自 AWS S3 存储桶的图像?
- java - 检查数据库数据
- c++ - C++ - 我应该使用 Dijkstra 的哪些数据结构?