如何从View调用下一个ActiveRecord条目实现无重复题目切换?
解决思路:用Session保存状态 + AJAX无刷新更新
你的核心问题在于HTTP是无状态的——每次点击按钮发起请求时,控制器都会重新生成一个新的实例,之前的shuffled_questions数组根本没法跨请求保留,所以每次都会重新洗牌,自然没法实现"连续取不重复题目"的需求。下面是完整的解决方案,一步步来:
1. 重构控制器:用Session保存洗牌后的题目ID数组
我们把洗牌后的ID数组存在用户的Session里,这样每次请求都能复用同一个数组,保证题目不重复:
class QuestionsController < ApplicationController def practice # 第一次访问时,初始化会话中的洗牌题目ID数组(如果不存在的话) session[:shuffled_question_ids] ||= Question.ids.shuffle # 取出数组中的第一个题目作为初始展示 @question = Question.find(session[:shuffled_question_ids].first) end # 新增处理下一题的动作,专门响应AJAX请求 def next_question # 从Session数组中移除并取出下一个题目ID(shift取开头/pop取末尾,看你需求) next_id = session[:shuffled_question_ids].shift if next_id @question = Question.find(next_id) # 只渲染题目局部视图,不需要整页布局 render partial: 'question', locals: { question: @question }, layout: false else # 所有题目都做完了,返回提示文本 render plain: "所有题目都完成啦!点击重新开始再来一遍吧~", layout: false end end # 可选:重置练习(清空Session数组,重新洗牌) def reset_practice session[:shuffled_question_ids] = nil redirect_to practice_questions_path end end
2. 配置路由
在config/routes.rb里添加下一题和重置的路由:
Rails.application.routes.draw do get 'questions/practice', to: 'questions#practice' get 'questions/next', to: 'questions#next_question' get 'questions/reset', to: 'questions#reset_practice' # 其他你的路由... end
3. 提取题目局部视图
把原来展示题目的代码单独抽成局部视图app/views/questions/_question.html.erb,方便AJAX请求后复用:
<div id="question-card"> <h3><%= question.title %></h3> <div class="options"> <% question.options.each_with_index do |option, idx| %> <label> <input type="radio" name="answer" value="<%= idx %>"> <%= option.text %> </label> <% end %> </div> </div>
4. 修改主视图,添加AJAX按钮
在practice.html.erb里引入局部视图,并用JS绑定按钮的AJAX请求:
<h1>选择题练习</h1> <!-- 题目容器,用来接收AJAX返回的新题目 --> <div id="question-container"> <%= render partial: 'question', locals: { question: @question } %> </div> <div class="action-buttons"> <button id="next-btn">下一题</button> <button id="reset-btn">重新开始</button> </div> <script> // 下一题按钮逻辑 document.getElementById('next-btn').addEventListener('click', function() { fetch('/questions/next') .then(response => response.text()) .then(html => { document.getElementById('question-container').innerHTML = html; }) .catch(err => console.log('加载下一题失败:', err)); }); // 重置按钮逻辑 document.getElementById('reset-btn').addEventListener('click', function() { window.location.href = '/questions/reset'; }); </script>
关键细节说明
- Session的作用:每个用户的Session是独立的,所以多用户练习时不会互相干扰,且刷新页面后会保留当前的练习进度(如果需要刷新后重置,可以调整
practice动作的逻辑)。 - AJAX无刷新:用
fetchAPI发送请求,只更新页面上的题目容器,不用整页刷新,体验更流畅。 - 边界处理:当Session数组为空时,会返回"完成"提示,避免报错。
内容的提问来源于stack exchange,提问作者Martin
相关产品推荐
相关产品推荐

