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

多次调用视图时复用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 02:55:39