在我的应用程序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_a
或all
。这是一件好事,因为这意味着你可以继续构建一个查询,它只会在调用时才真正执行。
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
将导致立即执行此查询。