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

Django投票应用选票未统计、多问题页面提交及扩展问题类型的技术咨询

Django投票应用选票未统计、多问题页面提交及扩展问题类型的技术咨询

Hey there! Let's tackle your Django poll app issues one by one, starting with the most pressing vote-counting problem, then moving to tests and future extensions.

一、解决多问题页面投票不统计的问题

你的投票没被统计,核心有几个关键问题需要修正:

1. 表单提交目标错误

看你的index.html里,表单的action指向了polls:results,但实际处理投票逻辑的是vote视图!这就导致表单提交后直接跳转到结果页,根本没触发投票计数的代码。

修改index.html里的form标签:

<form action="{% url 'polls:vote' %}" method="post">

别忘了确保你的urls.py里已经配置了polls:vote的路由,比如:

path('vote/', views.vote, name='vote'),

2. 表单name属性与视图取值不匹配(稳定性问题)

你现在用name="{{ caty }}"(也就是Question的__str__返回的question_text)作为表单控件的name,但如果question_text里有空格、特殊字符,会导致request.POST无法正确取值。更稳妥的方式是用问题的id来标识:

修改index.html里的radio输入:

<input type="radio" name="question_{{ caty.id }}" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />

然后在vote视图里,对应地用问题id拼接的key来获取选中的选项id,同时增加异常处理避免崩溃:

@require_http_methods(["POST"])
def vote(request):
    questions = get_list_or_404(Question)
    for question in questions:
        # 拼接对应的表单key
        form_key = f"question_{question.id}"
        # 检查用户是否为该问题选择了选项
        if form_key not in request.POST:
            # 可以在这里处理未选择的情况,比如跳过或者返回错误提示
            continue
        try:
            selected_choice = question.choice_set.get(pk=request.POST[form_key])
            # 使用F()表达式避免并发更新的竞态条件(多用户同时投票时计数错误)
            selected_choice.votes = F('votes') + 1
            selected_choice.save()
        except Choice.DoesNotExist:
            # 处理选项不存在的异常,返回带错误提示的页面
            return render(request, 'polls/index.html', {
                'question_list': questions,
                'error_message': f"Invalid choice for question: {question.question_text}",
            })
    return HttpResponseRedirect(reverse("polls:results"))

3. 竞态条件修复

原来的selected_choice.votes +=1在多用户同时投票时可能导致计数错误,用Django的F()表达式可以直接在数据库层面更新,避免这个问题,上面的代码已经修正了这一点。

二、编写多问题投票的单元测试

要模拟多问题的表单提交,你需要先创建测试用的Question和Choice对象,再构造包含多个问题选项的POST数据:

修改你的tests.py里的测试方法:

def test_multiple_question_vote(self):
    # 创建测试用的两个问题和对应选项
    q1 = Question.objects.create(question_text="Favorite color?", pub_date=timezone.now())
    q1_choice1 = Choice.objects.create(question=q1, choice_text="Red", votes=0)
    q1_choice2 = Choice.objects.create(question=q1, choice_text="Blue", votes=0)
    
    q2 = Question.objects.create(question_text="Favorite food?", pub_date=timezone.now())
    q2_choice1 = Choice.objects.create(question=q2, choice_text="Pizza", votes=0)
    q2_choice2 = Choice.objects.create(question=q2, choice_text="Burger", votes=0)
    
    client = Client()
    # 构造POST数据,对应每个问题的选中选项
    post_data = {
        f"question_{q1.id}": q1_choice1.id,
        f"question_{q2.id}": q2_choice2.id,
    }
    # 提交到vote视图,follow=True跟踪重定向
    response = client.post(reverse('polls:vote'), post_data, follow=True)
    
    # 检查是否跳转到结果页
    self.assertRedirects(response, reverse('polls:results'))
    
    # 重新从数据库获取选项,检查投票数是否增加
    q1_choice1.refresh_from_db()
    q2_choice2.refresh_from_db()
    self.assertEqual(q1_choice1.votes, 1)
    self.assertEqual(q2_choice2.votes, 1)

三、扩展问题类型(自由文本等)的方案选择

关于是否新建app还是修改现有models,取决于你的需求复杂度:

方案1:修改现有models(适合轻量需求)

如果自由文本问题和选择题属于同一类问卷内容,不需要完全独立管理,可以:

  • 给Question模型新增一个类型字段,标记是选择题还是自由文本:
class Question(models.Model):
    QUESTION_TYPES = [
        ('CHOICE', 'Choice'),
        ('TEXT', 'Free Text'),
    ]
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField("date published")
    question_type = models.CharField(max_length=10, choices=QUESTION_TYPES, default='CHOICE')
    
    # 原有方法保持不变...
  • 新增一个TextResponse模型,存储用户的自由文本回答:
class TextResponse(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    response_text = models.TextField()
    submitted_at = models.DateTimeField(auto_now_add=True)

然后在模板里根据question_type渲染不同的表单控件(radio组或者textarea),在vote视图里分别处理选择题计数和文本回答的存储。

方案2:新建独立app(适合复杂需求)

如果自由文本问卷和选择题问卷是完全独立的业务,或者未来会有更多不同类型的问题,新建一个surveys或者text_pollsapp会更清晰,避免把不同业务逻辑混在一个app里。不过如果你的表单比较静态,这个方案可能有点过重,方案1更适合你。

另外,你提到有人警告不要在html里硬编码问题/选项,这是对的,但你现在通过Django模板动态渲染控件的方式是没问题的——只需要维护数据库里的问题和选项,不需要修改模板代码,完全符合最佳实践。

备注:内容来源于stack exchange,提问作者pizzapizzapizza

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.21 14:24:35