【问题标题】:as_json ignoring second part of .where() clauseas_json 忽略 .where() 子句的第二部分
【发布时间】:2017-03-27 21:53:36
【问题描述】:

我正在尝试从包含 5 个表的现有数据库中生成 json 结构: :用户 :资源 :quiz_questions :quiz_answers :quiz_responses

一个资源有很多 quiz_questions,它有很多 quiz_answer,反过来又有很多 quiz_responses。用户也有_many quiz_responses。 (这个想法是用户进行多项选择测验,然后他们选择现有答案,这反过来在 quiz_responses 中创建一个新行。

所以我有两行代码:

questions = QuizQuestion.includes(:quiz_answers, :quiz_responses).where(resource_id: 623, quiz_responses: {user_id: 18276})

产生这个查询:

SELECT 
  `quiz_questions`.`id` AS t0_r0,
  `quiz_questions`.`question` AS t0_r1
  `quiz_questions`.`resource_id` AS t0_r2, 
  `quiz_questions`.`created_at` AS t0_r3, 
  `quiz_questions`.`updated_at` AS t0_r4, 
  `quiz_questions`.`question_type` AS t0_r5, 
  `quiz_questions`.`url` AS t0_r6, 
  `quiz_questions`.`auto_next` AS t0_r7, 
  `quiz_questions`.`show_correct` AS t0_r8, 
  `quiz_questions`.`answer_type` AS t0_r9, 
  `quiz_answers`.`id` AS t1_r0, 
  `quiz_answers`.`answer` AS t1_r1, 
  `quiz_answers`.`quiz_question_id` AS t1_r2, 
  `quiz_answers`.`correct` AS t1_r3, 
  `quiz_answers`.`created_at` AS t1_r4, 
  `quiz_answers`.`updated_at` AS t1_r5, 
  `quiz_answers`.`answer_immediately` AS t1_r6, 
  `quiz_answers`.`time_limit` AS t1_r7, 
  `quiz_responses`.`id` AS t2_r0, 
  `quiz_responses`.`user_id` AS t2_r1, 
  `quiz_responses`.`quiz_answer_id` AS t2_r2, 
  `quiz_responses`.`created_at` AS t2_r3, 
  `quiz_responses`.`updated_at` AS t2_r4, 
  `quiz_responses`.`attempt_id` AS t2_r5, 
  `quiz_responses`.`video_url` AS t2_r6, 
  `quiz_responses`.`correct` AS t2_r7, 
  `quiz_responses`.`group_id` AS t2_r8 
FROM `quiz_questions` 
  LEFT OUTER JOIN `quiz_answers` ON `quiz_answers`.`quiz_question_id` = `quiz_questions`.`id` 
  LEFT OUTER JOIN `quiz_answers` `quiz_answers_quiz_questions_join` ON `quiz_answers_quiz_questions_join`.`quiz_question_id` = `quiz_questions`.`id` 
  LEFT OUTER JOIN `quiz_responses` ON `quiz_responses`.`quiz_answer_id` = `quiz_answers_quiz_questions_join`.`id` 
WHERE 
  `quiz_questions`.`resource_id` = 623 
  AND `quiz_responses`.`user_id` = 18276

第二行代码:

questions.as_json(include: { quiz_answers: { include: [:quiz_responses]}})

调用这些额外的查询:

  QuizResponse Load (0.8ms)  SELECT `quiz_responses`.* FROM `quiz_responses`  WHERE `quiz_responses`.`quiz_answer_id` = 755

  QuizResponse Load (0.8ms)  SELECT `quiz_responses`.* FROM `quiz_responses`  WHERE `quiz_responses`.`quiz_answer_id` = 756

  QuizResponse Load (1.5ms)  SELECT `quiz_responses`.* FROM `quiz_responses`  WHERE `quiz_responses`.`quiz_answer_id` = 757

  QuizResponse Load (0.7ms)  SELECT `quiz_responses`.* FROM `quiz_responses`  WHERE `quiz_responses`.`quiz_answer_id` = 758

  QuizResponse Load (0.6ms)  SELECT `quiz_responses`.* FROM `quiz_responses`  WHERE `quiz_responses`.`quiz_answer_id` = 759

  QuizResponse Load (0.7ms)  SELECT `quiz_responses`.* FROM `quiz_responses`  WHERE `quiz_responses`.`quiz_answer_id` = 760

  QuizResponse Load (0.6ms)  SELECT `quiz_responses`.* FROM `quiz_responses`  WHERE `quiz_responses`.`quiz_answer_id` = 761

  QuizResponse Load (0.6ms)  SELECT `quiz_responses`.* FROM `quiz_responses`  WHERE `quiz_responses`.`quiz_answer_id` = 764

  QuizResponse Load (0.8ms)  SELECT `quiz_responses`.* FROM `quiz_responses`  WHERE `quiz_responses`.`quiz_answer_id` = 765

此代码的预期目标是获取与资源 867 相关的所有问题,然后返回可能的答案以及与特定用途相关的响应(在本例中为 18276)

问题在于,虽然仅返回与资源 867 相关的问题 as_json,但所有用户的响应都会返回,尽管第一行中有 where 子句,而不仅仅是用户 18276 的响应。为什么是这样?有什么方法可以告诉 as_json 只使用它收到的内容并返回在初始查询中选择的 quiz_responses 吗?解决此问题的“rails 方式”是什么?

【问题讨论】:

  • 检查活动模型序列化器,为这个响应创建一个序列化器。它应该将您的问题分解为小对象,并减轻这一衬里的痛苦。

标签: mysql ruby-on-rails ruby ruby-on-rails-4 activerecord


【解决方案1】:

更新:新的解决方案

Rails 不允许预加载参数化关联。但是可以通过两个查询加载所有必要的数据并将其解析为json:

# load data
questions = QuizQuestion.preload(:quiz_answers)
answers_ids = questions.collect{ |question| question.quiz_answers.collect(&:id) }.flatten
responses = QuizResponse.where(quiz_answer_id: answers_ids).group_by(&:quiz_answer_id)

# making json
json_string = Jbuilder.encode do |json|
  json.array! questions do |question|
    json.merge! question.attributes

    json.quiz_answers question.quiz_answers do |answer|
      json.merge! answer.attributes

      json.quiz_responses responses[answer.id] do |response|
        json.merge! response.attributes
      end
    end
  end
end

如果该代码没有针对问题中的用户进行 quiz_response,则此代码不会从结果中省略 quiz_answer。

第一版:

数据结构有两个quiz_responses关联:

问答题has_many :quiz_responses, through: :quiz_answers 测验答案has_many :quiz_responses 查询包括 quiz_responses 作为 QuizQuestion 的关系。但是as_json 使用 quiz_responses 作为 QuizAnswer 的关系。而且 Rails 不够聪明,无法理解已经加载了必要的 quiz_responses。

你需要做的就是以嵌套的方式重写包含,就像你想使用它一样:

questions = QuizQuestion.includes(quiz_answers: :quiz_responses)

【讨论】:

  • 非常有帮助!!,但是这个解决方案引入了一个不同的问题,如果 quiz_answer 没有针对相关用户的 quiz_responses,它会从 json 中的数组中省略。有没有一种方法可以与我想要所有 quiz_answers 的 Rails 进行交流,无论相关用户是否有 quiz_responses?
  • 在 MySQL 中玩耍,看起来我需要这样的东西:SELECT * FROM quiz_questions LEFT OUTER JOIN quiz_answers ON quiz_answers.quiz_question_id = quiz_questions.id LEFT OUTER JOIN quiz_responses ON quiz_responses.quiz_answer_id = quiz_answers.id AND quiz_responses.user_id = 18276 AND quiz_responses.attempt_id = 2 WHERE quiz_questions.resource_id = 623 order by quiz_questions.id
  • 所以这是 Rails 无法生成所需 SQL 查询的情况?
  • Rick James,是的,Rails 无法为这种关联进行预加载。
  • llya,你能把你以前的答案和你现在起床的答案都放上来吗?为了后代?下一个版本的 rails 出来时,我可能想重新审视这个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-31
  • 1970-01-01
  • 2014-03-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多