为什么 Rails 在视图中为"include?"调用生成 SQL 查询,而不是在控制台中生成 SQL 查询?



在我的应用程序a Position has_many Questions。在我的位置编辑视图中,我通过循环Questions数组并检查当前位置已经包含的复选框来创建一组复选框。

看起来像这样:

- @questions.each do |question|
  = check_box_tag :question_ids, question.id, @position.questions.include?(question), name: 'position[question_ids][]', id: "question_check_#{question.id}"
  = label_tag "question_check_#{question.id}", question.text

我注意到加载这个页面花了很长时间,我的日志中有这些(我已经把它剪掉了):

LOG: duration: 827.370 ms statement: SELECT 1 FROM "questions" INNER JOIN "required_questions" ON "questions"."id" = "required_questions"."question_id" WHERE "required_questions"."position_id" IS NULL AND "questions"."id" = 1 ORDER BY account_id desc, "questions".id asc LIMIT 1
LOG: duration: 821.666 ms statement: SELECT 1 FROM "questions" INNER JOIN "required_questions" ON "questions"."id" = "required_questions"."question_id" WHERE "required_questions"."position_id" IS NULL AND "questions"."id" = 2 ORDER BY account_id desc, "questions".id asc LIMIT 1
LOG: duration: 713.379 ms statement: SELECT 1 FROM "questions" INNER JOIN "required_questions" ON "questions"."id" = "required_questions"."question_id" WHERE "required_questions"."position_id" IS NULL AND "questions"."id" = 3 ORDER BY account_id desc, "questions".id asc LIMIT 1

我跟踪问题到以下代码:

@position.questions.include?(question)

我认为这是一个简单的数组。所以我对我的发现感到惊讶。当我通过添加显式的.to_a调用来修复它时,我也感到惊讶:

@position.questions.to_a.include?(question)

当我第三次在Rails控制台中测试原始代码时,我很惊讶,没有生成SQL查询。

在这一点上,我只是好奇-为什么我的原始代码为每次迭代生成SQL查询时,相同的代码在Rails控制台(尽管没有迭代和内部Rails控制台)没有?为什么不做一个Array.include?检查?

这是因为@position.questions不是一个数组。它实际上是一个ActiveRecord::Relation。问题是控制台的行为实际上与服务器应用程序不同。

例如,在控制台中,position.questions返回一个数组。这是因为控制台实际上将这个表达式计算为position.questions.all,它相当于position.questions.to_a。在服务器应用程序中,直到实际使用查询时才调用to_aall。这是一件好事,因为这意味着你可以继续构建一个查询,它只会在调用时才真正执行。

例如:

query = @position.questions
query.first
query.last

将实际生成两个查询,返回服务器应用程序中的两条记录,因为query变量将被分配ActiveRecord::Relation而不是Array。在控制台中,这将生成一个查询,但该查询将把所有问题加载到Array中,并将其分配给query,然后选择第一个和最后一个元素。

all, first, last, count甚至include?关键字都是实际执行查询的触发器,因此在您的应用程序中,当您调用@position.questions.include?时,您正在对@position.questions关系执行单个查询。添加to_a将导致立即执行此查询。

最新更新