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
相关产品推荐
相关产品推荐

