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
相关产品推荐
相关产品推荐

