如何在Django测验中实现无重复的题目随机排序?
解决Django测验随机出题重复的问题
你遇到的重复问题根源在于每次请求都调用order_by('?')重新随机排序,而视图依赖user_answers.count()作为当前题目索引。两次请求的排序结果不同,会导致索引对应的题目重复或跳过。
解决方案:为每个用户的测试生成固定的随机题目顺序,并保存到UserTest模型中,后续所有请求都复用这个顺序。
1. 修改UserTest模型
添加字段存储随机排序的题目ID列表:
# models.py from django.db import models from django.contrib.auth.models import User class UserTest(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) test_set = models.ForeignKey(TestSet, on_delete=models.CASCADE) random_question_order = models.JSONField(default=list) # 存储随机排序的题目ID # 保留原有的其他字段
2. 调整视图逻辑
在创建UserTest时生成固定随机顺序,后续请求复用该顺序:
@login_required def test(request, test_id): test_set = get_object_or_404(TestSet, id=test_id) user_test, created = UserTest.objects.get_or_create(user=request.user, test_set=test_set) # 首次创建测试时,生成并保存随机题目顺序 if created: question_ids = list(test_set.question_set.values_list('id', flat=True)) import random random.shuffle(question_ids) user_test.random_question_order = question_ids user_test.save() # 根据保存的顺序获取题目(保持固定排序) questions = test_set.question_set.filter(id__in=user_test.random_question_order).order_by( models.Case( *[models.When(id=q_id, then=pos) for pos, q_id in enumerate(user_test.random_question_order)] ) ) user_answers = user_test.useranswer_set.all() correct_answers = sum(user_answer.selected_answer.is_correct for user_answer in user_answers) current_question_index = user_answers.count() find_percent = int(int(correct_answers) / len(questions) * 100) num_questions = questions.count() if current_question_index < num_questions: # 从固定顺序中取当前题目 current_question_id = user_test.random_question_order[current_question_index] current_question = test_set.question_set.get(id=current_question_id) if request.method == 'POST': form = QuestionForm(request.POST, question=current_question) if form.is_valid(): user_answer = form.cleaned_data['selected_answer'] user_test.useranswer_set.update_or_create(question=current_question, defaults={'selected_answer': user_answer}) return redirect('test', test_id=test_id) else: form = QuestionForm(question=current_question) else: return render(request, 'test_view.html', {'test_set': test_set, 'user_test': user_test, 'questions': questions, 'show_result': True, 'current_question': None, 'correct_answers': correct_answers, 'find_percent': find_percent}) return render(request, 'test_view.html', {'test_set': test_set, 'form': form, 'current_question_index': current_question_index, 'current_question': current_question, 'correct_answers': correct_answers})
原理说明
- 首次启动测试时,生成一次随机题目ID序列并持久化到
UserTest中,确保每个用户的测试顺序固定。 - 后续请求通过
id__in和Case/When保持题目顺序与保存的序列一致,避免重复排序导致的题目重复。 - 用保存的序列索引获取当前题目,保证进度逻辑稳定。
内容的提问来源于stack exchange,提问作者Ubuty_programmist_7
相关产品推荐
相关产品推荐

