多次调用视图时复用Queryset:避免重复查询数据库的方案
解决HTMX问答场景重复查询数据库的问题
方案一:用Django Session存储问题列表
第一次加载页面时一次性查询所有问题,序列化后存入Session,后续HTMX请求直接从Session读取,无需重复查库。
修改views.py:
from django.core import serializers @login_required def question(request): # 首次GET请求时查询并缓存问题到Session if 'questions' not in request.session: questions = Question.objects.all() # 序列化为字典列表存入Session request.session['questions'] = serializers.serialize('python', questions) if request.method == 'POST': answer = request.POST.get("answer") current_question_num = int(request.POST.get("questionNumber")) # 从Session取出问题列表 questions_data = request.session['questions'] # 处理下一题逻辑 next_question_idx = current_question_num + 1 context = {} if next_question_idx < len(questions_data): next_question = questions_data[next_question_idx] # 提取问题字段(序列化后的数据包含model和fields键) context = { 'question': next_question['fields'], 'question_number': next_question_idx } else: # 所有题目完成的提示 context = {'finished': True} return render(request, 'interview/question.html', context) # 首次GET请求返回第一题 first_question = request.session['questions'][0] context = { 'question': first_question['fields'], 'question_number': 0 } return render(request, 'interview/question.html', context)
修改question.html的hx-vals,无需传递整个问题列表:
<input type="submit" value="Next Question" class="is-fullwidth pg-button-secondary" hx-post="{% url 'interview:next_question' %}" hx-target="#questionDiv" hx-include="[name='answer']" hx-vals='{"questionNumber": "{{ question_number }}"}' hx-swap="innerHTML">
方案二:前端一次性加载所有问题,JS控制流程
把所有问题提前渲染到前端,用JS管理当前题目索引,每次仅提交答案和题号给后端,后端无需查询所有问题。
修改视图views.py:
from django.utils.html import json_script @login_required def question(request): if request.method == 'POST': answer = request.POST.get("answer") current_question_num = int(request.POST.get("questionNumber")) # 这里仅处理答案逻辑(比如验证、记录),无需查询问题 # 返回下一题索引 return JsonResponse({'next_question_num': current_question_num + 1}) # 首次GET请求查询所有问题,序列化为JSON传给前端 questions = list(Question.objects.values('question_number', 'content')) # 只取需要的字段 context = { 'questions_json': json_script(questions, 'questions-data'), 'initial_question': questions[0], 'initial_question_num': 0 } return render(request, 'interview/question.html', context)
前端模板question.html:
{{ questions_json }} <div id="questionDiv"> <p>{{ initial_question.content }}</p> <form id="questionForm"> {% csrf_token %} <input class="input" name="answer" type="text"><br><br> <input type="submit" value="Next Question" class="is-fullwidth pg-button-secondary"> </form> </div> <script> const allQuestions = JSON.parse(document.getElementById('questions-data').textContent); let currentQuestionNum = {{ initial_question_num }}; function bindSubmitEvent() { document.getElementById('questionForm').addEventListener('submit', function(e) { e.preventDefault(); const answer = this.querySelector('[name="answer"]').value; htmx.ajax('POST', "{% url 'interview:next_question' %}", { target: '#questionDiv', swap: 'innerHTML', values: { 'answer': answer, 'questionNumber': currentQuestionNum }, headers: {'X-CSRFToken': '{{ csrf_token }}'}, onSuccess: function() { currentQuestionNum++; // 更新问题展示 if (currentQuestionNum < allQuestions.length) { const nextQuestion = allQuestions[currentQuestionNum]; document.getElementById('questionDiv').innerHTML = ` <p>${nextQuestion.content}</p> <form id="questionForm"> {% csrf_token %} <input class="input" name="answer" type="text"><br><br> <input type="submit" value="Next Question" class="is-fullwidth pg-button-secondary"> </form> `; bindSubmitEvent(); } else { document.getElementById('questionDiv').innerHTML = '<p>所有题目已完成!</p>'; } } }); }); } bindSubmitEvent(); </script>
方案三:用Django缓存缓存问题列表
如果问题数据不频繁更新,可通过Django缓存框架缓存问题列表,避免重复查询数据库。
from django.core.cache import cache @login_required def question(request): # 从缓存取问题,无缓存则查询并存入 questions = cache.get('all_interview_questions') if not questions: questions = list(Question.objects.all()) # 缓存1小时(3600秒),可按需调整时长 cache.set('all_interview_questions', questions, 3600) if request.method == 'POST': answer = request.POST.get("answer") current_question_num = int(request.POST.get("questionNumber")) next_question = questions[current_question_num + 1] context = {'question': next_question, 'question_number': current_question_num + 1} return render(request, 'interview/question.html', context) # 首次GET请求返回第一题 context = {'question': questions[0], 'question_number': 0} return render(request, 'interview/question.html', context)
你之前方法失败的原因
直接把{{ questions }}传入hx-vals,模板渲染后得到的是Django QuerySet的字符串形式(如<QuerySet [<Question: Question object (1)>, ...]>),这不是结构化数据,后端无法还原为Question对象,必须通过序列化(转JSON或字典列表)才能正确传递数据。
内容的提问来源于stack exchange,提问作者LBJ33
相关产品推荐
相关产品推荐

