我有以下模型:
class Exam < ActiveRecord::Base
belongs_to :user
has_many :exam_questions, dependent: :destroy
has_many :question_responses
has_many :questions, through: :exam_questions
class Question < ActiveRecord::Base
belongs_to :user
has_many :exam_questions
has_many :question_responses
has_many :exams, through: :exam_questions
has_many :answers, dependent: :destroy
end
class QuestionResponse < ActiveRecord::Base
belongs_to :user
belongs_to :exam
belongs_to :question
belongs_to :answer
end
每个exam
都创建一次每个user
。这意味着每个考试都是唯一的。但是,两个不同的考试可以有相同的问题。例如,假设我有10个问题(q1,q2,... q10)。考试1可以具有(q1,q2,q3,q4),考试2可以具有(q3,q4,q5,q6)。
QuestionResponse
是用户进行检查时的响应。
我希望能够保存用户的question_responses,并且如果用户仅回答了一半考试并在第二天重新开始考试,他/她将显示未回答的问题以及已回答的问题。
因此,我有这个:
exams_controller.rb
def show
@exam = Exam.find(params[:id])
@questions = @exam.questions
end
exams / show.html.erb
<!--//TODO: Too much logic here}-->
<section class="container">
<div class="text-center">
<% i = 1 %>
<% @questions.find_each do |q| %>
Question <%= i %>
<% if !q.question_responses.where("question_responses.user_id = ?", current_user.id).present? %>
<div class=" text-left word-break">
<h2><b><%= q.description %></b></h2>
</div>
<%= image_tag q.image.url(:medium) if !q.image.blank? %>
<div class="text-left">
<% q.answers.randomize_answers.each do |a| %>
<p class="answers" onclick="questionResponse(<%= @exam.id %>,<%= q.id %>, <%= a.id %>)">
<%= a.description %>
</p>
<% end %>
</div>
<% else %>
<% q.question_responses.where("question_responses.user_id = ?", current_user.id).find_each do |qr| %>
<div class=" text-left word-break">
<% question = qr.question %>
<h2><b><%= question.description %></b></h2>
</div>
<%= image_tag question.image.url(:medium) if !question.image.blank? %>
<div class="text-left">
<% question.answers.randomize_answers.each do |a| %>
<% if qr.answer.eql?(a) %>
<p class="selected-answers" onclick="questionResponse(<%= @exam.id %>,<%= question.id %>, <%= a.id %>)">
<%= a.description %>
</p>
<% else %>
<p class="answers" onclick="questionResponse(<%= @exam.id %>,<%= question.id %>, <%= a.id %>)">
<%= a.description %>
</p>
<% end %>
<% end %>
</div>
<% end %>
<% end %>
<% i+=1 %>
<% end %>
</div>
</section>
基本上,我正在做的是在考试中获取所有问题,并检查其是否具有question_responses
。如果没有,我将显示类answers
的问题答案。如果是,我得到question_responses
'的答案,并与班级answers-selected
一起显示。
这是可行的,但我认为逻辑根本不好。我应该更改表之间的关系吗?我应该在控制器中编写更好的查询吗?
编辑:
这是我的Answer
模型:
class Answer < ActiveRecord::Base
belongs_to :question
has_many :question_responses
scope :randomize_answers, ->{order('random()')}
end
更清楚一点:用户创建要发送到问题库的问题。另一个用户为其自己创建一个考试。创建考试后,将填充exam_questions
:
exams_controller.rb
def create
@new_exam = current_user.exams.new(exam_params)
@new_exam.save!
ExamQuestion.create_exam_questions(@new_exam.id, @new_exam.category, 3)
if @new_exam.save!
redirect_to exams_path
flash[:notice] = "You've successfully created a new exam!"
end
end
exam_question.rb
class ExamQuestion < ActiveRecord::Base
belongs_to :exam
belongs_to :question
def self.create_exam_questions(exam_id, category, n_questions)
random_questions = Question.where(category: category).random_records(n_questions)
random_questions.each do |rq|
ExamQuestion.create(exam_id: exam_id, question_id: rq.id)
end
end
end
答案 0 :(得分:1)
您没有显示您的Answer
模型,但让我们假设它看起来像:
# == Schema Information
#
# Table name: answers
#
# id :bigint not null, primary key
# question_response_id :bigint
# created_at :datetime not null
# updated_at :datetime not null
#
class Answer < ApplicationRecord
belongs_to :question_response
end
让我们假设您的QuestionResponse
看起来更像:
# == Schema Information
#
# Table name: question_responses
#
# id :bigint not null, primary key
# exam_question_id :bigint
# created_at :datetime not null
# updated_at :datetime not null
#
class QuestionResponse < ApplicationRecord
belongs_to :exam_question
has_one :answer
class << self
def with_answer
joins(:answer).where(answer: Answer.all)
end
end
end
现在,让我们假设您的ExamQuestion
(来自您先前的问题)如下所示:
# == Schema Information
#
# Table name: exam_questions
#
# id :bigint not null, primary key
# exam_id :bigint
# question_id :bigint
# created_at :datetime not null
# updated_at :datetime not null
#
class ExamQuestion < ApplicationRecord
belongs_to :exam
belongs_to :question
has_one :question_response
class << self
def for_question_responses(question_responses)
joins(:question_response).where(question_response: question_responses)
end
def with_question_responses
for_question_responses(QuestionResponse.with_answer)
end
end
end
Question
如下:
# == Schema Information
#
# Table name: questions
#
# id :bigint not null, primary key
# user_id :bigint
# created_at :datetime not null
# updated_at :datetime not null
#
class Question < ApplicationRecord
belongs_to :user
has_many :exam_questions
has_many :exams, through: :exam_questions
class << self
def with_answers
joins(:exam_questions).
where(exam_questions: {id: ExamQuestion.with_question_responses})
end
end
end
最后,Exam
可能类似于:
# == Schema Information
#
# Table name: exams
#
# id :bigint not null, primary key
# user_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
class Exam < ApplicationRecord
belongs_to :user
has_many :exam_questions
has_many :questions, through: :exam_questions
def questions_with_answer
questions.with_answers
end
def questions_with_no_answer
questions.
where.
not(id: questions_with_answer)
end
end
然后,在您的exams_controller.rb
中,您可以执行以下操作:
exams_controller.rb
def show
@exam = Exam.find(params[:id])
@exam_questions_with_answer = @exam.questions_with_answer
@exam_questions_with_no_answer = @exam.questions_with_no_answer
end
然后,在您的视图中遍历@exam_questions_with_answer
和@exam_questions_with_no_answer
变得无足轻重。
自然地,我掩盖了一些细节(例如q.answers.randomize_answers
),但我想你明白了。
答案 1 :(得分:1)
我会进行一些模式更改。
您拥有属于用户的考试,问题和问题响应。这允许矛盾。您可以使用与考试不同的用户来拥有QuestionResponse。问题属于许多考试,他们根本不应该有用户,除非是由用户来撰写问题?相反,考试应该属于用户,而QuestionResponses应该通过考试来吸引其用户。
我还将更改QuestionResponse以直接引用ExamQuestions,而不是多余地存储考试和问题。这样可以避免对不在考试中的问题有一个QuestionResponse。
这提出一个设计问题:如果用户有两个考试且每个考试都有相同的问题怎么办?他们两次考试都得到一个QuestionResponse吗?还是每次考试有问题解答?也就是说,他们是否必须在两次考试中都回答相同的问题?
我会添加一些独特的约束条件来验证某些假设。
class Exam < ApplicationRecord
belongs_to :user
has_many :exam_questions, dependent: :destroy
has_many :questions, through: :exam_questions
has_many :question_responses, through: :exam_questions
end
class Question < ApplicationRecord
has_many :exam_questions
has_many :exams, through: :exam_questions
has_many :question_responses, through: :exam_questions
has_many :answers, dependent: :destroy
end
class ExamQuestion < ApplicationRecord
belongs_to :exam
belongs_to :question
has_many :question_responses
has_one :user, through: :exam
end
class QuestionResponse < ApplicationRecord
belongs_to :exam_question
has_one :question, through: :exam_question
belongs_to :answer
has_one :exam, through: :exam_question
has_one :user, through: :exam
end
q.question_responses.where("question_responses.user_id = ?", current_user.id)
这似乎不正确。它将在任何考试中找到用户对问题的所有答复。但这是单个考试的显示页面,不是只显示用户对此考试的回答吗?
将其逻辑推入“问题”模型。
has_many :question_responses, through: :exam_questions do
def find_by_user(user)
joins(:exam).where("exams.user_id": user.id)
end
end
def response_for_exam(exam)
question_responses.joins(:exam).find_by("exams.id": exam.id)
end
现在,您的视图变得更加简单。像这样的东西。
<%= @questions.find_each.each_with_index do |question, i| %>
Question <%= i + 1 %>
<%=
response = question.response_for_exam(@exam)
if response
render partial: "question_with_response", locals: {question: question, response: response}
else
render partial: "question_without_response", locals: {question: question}
end
%>
end
使用view partials可以减少一个视图中的代码量。 Enumerable#each_with_index负责为我们计算问题。
请注意,这是一个1 + N的查询:我们先查询问题,然后针对每个问题查询其答案。