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

Django非标准投票应用:选票生成方案可行性与实现咨询

Django投票应用选票生成方案解答

1. 方案可行性分析

你的方案是可行的,但存在几个需要注意的问题:

  • 并发风险:多个用户同时提交候选人时,可能出现多张Ballot被重复填充的情况,需要添加并发控制逻辑避免冲突。
  • 扩展性差:硬编码cand0/cand1/cand2字段的设计,如果后续需要调整单张选票的候选人数量,修改成本很高。
  • 数据冗余:Candidate已经通过外键关联Ballot,Ballot中再存储候选字段属于冗余设计,可能导致数据不一致问题。

如果能接受上述限制,这个方案可以落地;如果追求更灵活的设计,建议调整模型结构(见下文优化建议)。

2. 具体实现步骤

第一步:调整模型结构

先完善Ballot模型的候选字段,使用外键关联Candidate:

import uuid
from django.db import models

class Candidate(models.Model):
    link = models.URLField()
    ballot = models.ForeignKey('Ballot', null=True, on_delete=models.SET_NULL)
    # 其他字段

    def __str__(self):
        return self.link

class Ballot(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    cand0 = models.ForeignKey(Candidate, null=True, blank=True, on_delete=models.SET_NULL, related_name='ballot_cand0')
    cand1 = models.ForeignKey(Candidate, null=True, blank=True, on_delete=models.SET_NULL, related_name='ballot_cand1')
    cand2 = models.ForeignKey(Candidate, null=True, blank=True, on_delete=models.SET_NULL, related_name='ballot_cand2')

    def is_full(self):
        """判断当前选票是否已填满3个候选人"""
        return self.cand0 is not None and self.cand1 is not None and self.cand2 is not None

    def __str__(self):
        return str(self.id)

第二步:实现表单提交的视图逻辑

在处理用户提交的视图中,添加事务和锁机制避免并发问题,同时完成候选人分配:

from django.db import transaction
from django.shortcuts import render, redirect
from .forms import UploadForm
from .models import Ballot, Candidate

def submit_candidate(request):
    if request.method == 'POST':
        form = UploadForm(request.POST)
        if form.is_valid():
            candidate = form.save(commit=False)
            # 开启原子事务,加锁防止并发修改
            with transaction.atomic():
                # 查找第一个未填满的选票并加锁
                ballot = Ballot.objects.select_for_update().filter(
                    models.Q(cand0__isnull=True) | models.Q(cand1__isnull=True) | models.Q(cand2__isnull=True)
                ).first()
                # 如果没有未填满的选票,新建一张
                if not ballot:
                    ballot = Ballot.objects.create()
                
                # 将候选人分配到选票的空字段
                if ballot.cand0 is None:
                    ballot.cand0 = candidate
                elif ballot.cand1 is None:
                    ballot.cand1 = candidate
                elif ballot.cand2 is None:
                    ballot.cand2 = candidate
                
                ballot.save()
                candidate.ballot = ballot
                candidate.save()
            return redirect('success_page')  # 替换为你的成功页路由
    else:
        form = UploadForm()
    return render(request, 'submit_candidate.html', {'form': form})

第三步:可选优化方案(更灵活的模型设计)

如果不想硬编码候选字段,可以去掉Ballot中的cand0-cand2,通过Candidate的外键关联来统计选票的候选人数量:

# 修改后的模型
class Candidate(models.Model):
    link = models.URLField()
    ballot = models.ForeignKey('Ballot', null=True, on_delete=models.SET_NULL, related_name='candidates')
    # 其他字段

class Ballot(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

    def is_full(self):
        return self.candidates.count() >= 3

    def __str__(self):
        return str(self.id)

对应的视图逻辑调整为:

def submit_candidate(request):
    if request.method == 'POST':
        form = UploadForm(request.POST)
        if form.is_valid():
            candidate = form.save(commit=False)
            with transaction.atomic():
                # 查找候选人数量不足3的选票并加锁
                ballot = Ballot.objects.select_for_update().annotate(
                    cand_count=models.Count('candidates')
                ).filter(cand_count__lt=3).first()
                if not ballot:
                    ballot = Ballot.objects.create()
                candidate.ballot = ballot
                candidate.save()
            return redirect('success_page')
    else:
        form = UploadForm()
    return render(request, 'submit_candidate.html', {'form': form})

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 14:35:26