在考试之外显示问题的答案和答案

时间:2019-08-01 20:42:07

标签: ruby-on-rails

我有以下模型:

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

2 个答案:

答案 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吗?还是每次考试有问题解答?也就是说,他们是否必须在两次考试中都回答相同的问题?

我会添加一些独特的约束条件来验证某些假设。

  • 每个ExamQuestion的一个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的查询:我们先查询问题,然后针对每个问题查询其答案。