You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails 6:单表单批量保存多条TestResult测试结果的实现方案

分步修改方案

1. 模型层补全验证

TestResult 补全关联字段的存在性校验,避免无效记录入库:

class TestResult < ApplicationRecord
  belongs_to :test_question

  # 新增test_question_id校验
  validates :answer, :test_question_id, presence: true
end

2. 控制器层批量逻辑改造

2.1 调整fetch_random_questions方法

保留题目的数据库ID,将抽中的题目存入session,避免提交失败后题面随机变更丢失用户已选内容:

def fetch_random_questions
  # 优先复用session中已抽取的题目(提交失败回显场景使用)
  @randomize_questions = session[:test_questions] || TestQuestion::CATEGORIES.map do |category|
    TestQuestion.where(question_category: category, published: true).order('RANDOM()').first
  end.map do |obj|
    next unless obj
    {
      id: obj.id, # 新增:保留题目的主键ID,用于提交时关联TestResult
      question: obj.question,
      answers: [obj.correct_answer, obj.other_answer1, obj.other_answer2, obj.other_answer3].shuffle,
    }
  end.compact

  # 校验抽题数量,不足5道直接报错提示配置
  raise '测试题目配置不完整,请联系管理员' unless @randomize_questions.size == 5
  # 存入session供后续提交、错误回显使用
  session[:test_questions] = @randomize_questions
end

2.2 改造create方法支持批量保存

def new
  # 无需初始化单个TestResult,直接使用抽好的题目即可
end

def create
  # 批量生成所有题目的作答记录
  @test_results = TestResult.create(test_result_params)

  if @test_results.all?(&:valid?)
    # 提交成功清除session中的题目缓存
    session.delete(:test_questions)
    # 替换为你实际需要跳转的路径,比如注册完成页
    redirect_to 你的目标跳转路径, notice: '法律测试提交成功'
  else
    # 提交失败回显new页面,复用session中的原题目
    render :new
  end
end

2.3 调整强参数允许批量作答字段

private

def test_result_params
  # 提取批量作答的hash值转数组,允许提交关联ID和作答内容
  params.require(:test_results).values.map do |result_param|
    result_param.permit(:test_question_id, :answer)
  end
end

3. 视图层单表单改造

删除循环生成多表单的逻辑,改为一个外层表单承载所有题目,仅保留一个提交入口:

<%# 全局错误提示 %>
<% if @test_results && @test_results.any? { |r| r.errors.any? } %>
  <div class="alert alert-danger">请完成所有题目作答后再提交</div>
<% end %>

<%# 单个外层表单,提交到test_results的create路径 %>
<%= simple_form_for :test_results, url: test_results_path, method: :post do |f| %>
  <% @randomize_questions.each_with_index do |q, index| %>
    <div class="question-block mb-4">
      <%# 展示题面 %>
      <h3><%= q[:question] %></h3>
      <%# 按索引生成批量字段,对应每道题的作答数据 %>
      <%= f.simple_fields_for index do |result| %>
        <%# 隐藏字段,存储当前题目的ID,提交时关联TestResult %>
        <%= result.input :test_question_id, as: :hidden, input_html: { value: q[:id] } %>
        <%# 单选按钮组,渲染当前题的选项 %>
        <%= result.input :answer, collection: q[:answers], as: :radio_buttons, label: false %>
        <%# 回显当前题的错误提示,比如未选择答案 %>
        <% if @test_results && @test_results[index]&.errors&.any? %>
          <p class="text-danger mt-1"><%= @test_results[index].errors.full_messages.join('、') %></p>
        <% end %>
      <% end %>
    </div>
  <% end %>

  <%# 唯一的提交按钮,中文文案 %>
  <%= f.button :submit, '提交答卷' %>
<% end %>

改造后效果

  • 整个页面仅一个提交按钮,点击一次即可提交所有5道题的作答
  • 每道题的作答会生成一条独立的TestResult记录,自动关联对应TestQuestion
  • 未完成作答时回显对应题目的错误提示,且题面不会随机变更,保留用户已选内容
  • 提交成功后清除session缓存,下次进入测试会重新抽题

内容的提问来源于stack exchange,提问作者mr_muscle

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.25 12:36:04