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

Django实现模型数据传视图及Survey提交发放10积分

问题说明

需要实现两个核心功能:

  • 将models.py中定义的问卷相关模型数据传递到视图层,最终渲染展示给用户
  • 用户每成功提交1份Survey问卷,自动为当前操作用户增加10个账户积分

现有代码存在两处核心缺失:一是模型关联不全、关联查询缺失,模板拿不到完整问卷数据;二是Participant模型未记录提交用户、提交逻辑未做登录校验也未调用已写好的积分发放方法。


具体修改步骤

第一步:修改models.py 补全模型关联

原有Participant模型只关联了问卷,没有记录提交用户,既无法精准给对应账户发积分,也无法防范用户重复提交刷积分。
修改后的Participant模型代码:

class Participant(models.Model):
    # 新增外键关联用户模型,配置反向查询名方便后续查用户的参与记录
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="survey_participations")
    survey = models.ForeignKey(Survey, on_delete=models.CASCADE)
    participation_datetime = models.DateTimeField('Participation DateTime')

    class Meta:
        # 增加联合唯一约束,同一个用户同一份问卷仅能提交一次
        unique_together = ('user', 'survey')

    def __str__(self):
        return f"Participant {self.user.username} {self.participation_datetime}"

注意:修改完模型后需要依次执行python manage.py makemigrations、python manage.py migrate命令同步数据库结构。

第二步:修改视图层逻辑,补全数据传递+积分发放能力

2.1 优化问卷详情标签逻辑,传递完整渲染数据

原有survey_details方法仅查询了Survey主表数据,没有关联加载对应的问题、选项,前端无法渲染完整问卷,同时缺少异常处理,问卷ID不存在时会直接抛500错误。
修改后的代码:

from django.shortcuts import get_object_or_404
from django.db.models import Prefetch

@register.inclusion_tag('survey/survey_details.html', takes_context=True)
def survey_details(context, survey_id):
    # 用get_object_or_404处理不存在的问卷ID,返回404而非服务错误
    # 预加载关联的问题、问题下的选项,减少重复数据库查询
    survey = get_object_or_404(
        Survey.objects.prefetch_related(
            Prefetch('question_set', queryset=Question.objects.prefetch_related('choice_set'))
        ),
        id=survey_id
    )
    request = context['request']
    # 额外传递当前用户、用户是否已参与的标记,方便模板做状态判断
    return {
        'survey': survey,
        'user': request.user,
        'has_participated': survey.participant_set.filter(user=request.user).exists() if request.user.is_authenticated else False
    }

2.2 重构问卷提交逻辑,增加校验+积分发放

原有提交逻辑没有登录校验、没有防重复提交、没有记录提交人、没有触发积分发放,修改后代码:

from django.contrib.auth.decorators import login_required
from django.http import HttpResponseForbidden
from django.db.models import F

# 加登录校验装饰器,未登录用户不允许提交问卷
@login_required
@require_http_methods(["POST"])
def submit_survey(request):
    form_data = request.POST.copy()
    form_items = list(form_data.items())
    form_items.pop(0)  # 移除列表首位的csrf token
    survey = None
    choice_list = []
    # 先校验所有提交选项的合法性,避免中途报错导致数据不一致
    for item in form_items:
        choice_str, choice_id = item
        choice_id = int(choice_id.split('/')[1])
        choice = Choice.objects.select_related('question__survey').get(id=choice_id)
        if survey is None:
            survey = choice.question.survey
        choice_list.append(choice)
    
    if survey is not None:
        # 重复提交校验,已参与过的用户直接返回禁止提示
        if Participant.objects.filter(user=request.user, survey=survey).exists():
            return HttpResponseForbidden("你已经提交过这份问卷,无法重复提交")
        
        # 批量更新选项票数
        for choice in choice_list:
            choice.votes = F('votes') + 1
            choice.save(update_fields=('votes',))
        
        # 写入参与记录
        Participant.objects.create(
            user=request.user,
            survey=survey,
            participation_datetime=timezone.now()
        )

        # 直接调用User模型已实现的give_coins方法,给当前用户加10积分
        request.user.give_coins(count=10)

    return redirect('/submit_success/')

第三步:模板渲染适配

在survey/survey_details.html模板中,可以直接遍历传入的问卷数据渲染页面,参考代码:

<div class="survey-container">
    <h2>{{ survey.name }}</h2>
    <p>发布时间:{{ survey.published_on|date:"Y-m-d H:i" }}</p>
    {% if has_participated %}
        <p>你已完成这份问卷,累计获得10积分</p>
    {% else %}
    <form method="POST" action="/submit_survey/">
        {% csrf_token %}
        {% for question in survey.question_set.all %}
        <div class="question-item">
            <p>{{ forloop.counter }}. {{ question.question_text }}</p>
            {% for choice in question.choice_set.all %}
            <label style="display:block;margin:8px 0;">
                <input type="radio" name="choice/{{ choice.id }}" value="{{ choice.id }}" required>
                {{ choice.choice_text }}
            </label>
            {% endfor %}
        </div>
        {% endfor %}
        <button type="submit" style="margin-top:16px;">提交问卷</button>
    </form>
    {% endif %}
    <p style="margin-top:20px;">当前账户总积分:{{ user.coins }}</p>
</div>

注意事项
  • 所有涉及用户资产变动的接口必须加登录校验,否则匿名用户提交无法关联账户发积分
  • 用户+问卷的联合唯一约束必须配置,否则用户可以通过重复提交刷取大量积分
  • 关联数据预加载可以有效减少数据库查询次数,明显提升页面加载速度

内容的提问来源于stack exchange,提问作者devo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 00:04:24