如何获取各投票项的所有候选人及解决数据展示与投票权限问题?
Django投票系统问题排查与修复
问题描述
- 需要获取每个投票项(Position)对应的所有候选人选项
- 当前创建了两个投票项,前端仅展示第二个投票项的候选人数据,第一个仅能在终端打印,无法传递到前端
- 需实现选民完成某一投票项后仍可参与其他投票项,已用Voter模型的ManyToManyField关联Position,但功能未实现
相关代码
Models代码
class Voter(models.Model): gender_type = (('male', 'Male'), ('female', 'Female')) admin = models.OneToOneField(CustomUser, on_delete=models.CASCADE) gender = models.CharField(max_length=10, choices=gender_type) country_region = models.CharField(max_length=255, choices=Countries.choices, default=Countries.Pakistan) phone = PhoneNumberField(blank=True, unique=True) otp = models.CharField(max_length=10, null=True) verified = models.BooleanField(default=False) voted = models.BooleanField(default=False) otp_sent = models.IntegerField(default=0) # Control how many OTPs are sent ip_address = models.GenericIPAddressField(unique=True, default='ABC') position = models.ManyToManyField('Position', blank=True) def __str__(self): return self.admin.first_name def get_voted_position(self): pass class Position(models.Model): ACTIVE = 0 INACTIVE = 1 status_choice = ((ACTIVE, 'Active'), (INACTIVE, 'Inactive')) name = models.CharField(max_length=50, unique=True) max_vote = models.IntegerField() priority = models.IntegerField() description = models.TextField(null=True, blank=True) status = models.PositiveBigIntegerField(default=0, choices=status_choice) def __str__(self): return self.name def participants(self): participants = 0 candidates = self.candidates.all() for candidate in candidates: participants += candidate.votes return participants class Candidate(models.Model): fullname = models.CharField(max_length=50) photo = models.ImageField(upload_to="candidates") bio = models.TextField() position = models.ForeignKey(Position, on_delete=models.CASCADE, related_name='candidates') # votes = models.IntegerField(default=0) def __str__(self): return self.fullname def calculate_votes(self): votes = self.votes.all() total_votes = 0 for vote in votes: total_votes += vote.counter return total_votes class Votes(models.Model): voter = models.ForeignKey(Voter, on_delete=models.CASCADE) position = models.ForeignKey(Position, on_delete=models.CASCADE, related_name='positions') candidate = models.ForeignKey(Candidate, on_delete=models.CASCADE, related_name='votes') counter = models.IntegerField(default=0) class BlockIP(models.Model): voter = models.ForeignKey(Voter, on_delete=models.CASCADE, related_name='ip') position = models.ForeignKey(Position, on_delete=models.CASCADE, related_name='positions_ip', null=True, blank=True) ip_address = models.GenericIPAddressField(default='abc') @receiver(post_save, sender=Voter) def default_to_non_active(sender, instance, created, **kwargs): if created: instance = BlockIP.objects.create(voter=instance, ip_address=instance.ip_address) instance.save()
视图函数代码
def index(request): context = {} instruction = "" candidate_data = [] positions = Position.objects.order_by('priority').all() for position in positions: query = position.candidates.all() if position.max_vote > 1: instruction = "You may select up to " + str(position.max_vote) + " candidates" else: instruction = "Select a candidate" context = { 'positions': positions, 'query': query, 'candidate_data': candidate_data, 'instruction': instruction, } return render(request, 'poll/index.html', context)
前端模板代码
<div class="row"> <div class="mt-5"> {% for position in positions %} {{ position.name }} <br> {{ instruction }} <br> {% endfor %} {% for c in query %} {{ c.fullname }} {{ c.id }} <p>{{ c.bio }}</p> <img src="/media/{{ c.photo }}" alt="" width="200" height="200"> {% endfor %} </div> </div>
问题修复方案
1. 展示所有投票项的候选人数据
当前视图中query变量在循环中被反复覆盖,最终仅保留最后一个投票项的候选人数据。需重构视图,将每个投票项的候选人、指令打包后传递给前端。
修改视图函数:
def index(request): positions_with_data = [] positions = Position.objects.order_by('priority').all() for position in positions: candidates = position.candidates.all() # 生成当前投票项的指令 instruction = f"You may select up to {position.max_vote} candidates" if position.max_vote > 1 else "Select a candidate" # 打包投票项相关数据 positions_with_data.append({ 'position': position, 'candidates': candidates, 'instruction': instruction }) context = {'positions_with_data': positions_with_data} return render(request, 'poll/index.html', context)
修改前端模板,遍历每个投票项的完整数据:
<div class="row"> <div class="mt-5"> {% for item in positions_with_data %} <h3>{{ item.position.name }}</h3> <p>{{ item.instruction }}</p> {% for c in item.candidates %} <div class="candidate-item"> {{ c.fullname }} (ID: {{ c.id }}) <p>{{ c.bio }}</p> <img src="/media/{{ c.photo }}" alt="{{ c.fullname }}" width="200" height="200"> </div> {% endfor %} <hr> {% endfor %} </div> </div>
2. 实现多投票项的投票权限控制
完善Voter模型的方法,用于跟踪已投投票项;在投票逻辑中添加权限校验,并更新选民的已投记录。
更新Voter模型:
class Voter(models.Model): # ... 保留原有字段 ... def get_voted_positions(self): return self.position.all() def has_voted_for_position(self, position): return position in self.get_voted_positions()
投票处理视图示例:
def cast_vote(request, position_id, candidate_id): voter = request.user.voter # 假设已通过登录关联Voter实例 position = Position.objects.get(id=position_id) candidate = Candidate.objects.get(id=candidate_id) if voter.has_voted_for_position(position): return HttpResponse("您已完成该投票项的投票") # 创建投票记录 Votes.objects.create(voter=voter, position=position, candidate=candidate, counter=1) # 更新已投投票项列表 voter.position.add(position) voter.save() return redirect('index')
前端模板中添加投票状态判断:
{% for item in positions_with_data %} <h3>{{ item.position.name }}</h3> {% if request.user.voter.has_voted_for_position(item.position) %} <p>您已完成该投票项的投票</p> {% else %} <p>{{ item.instruction }}</p> {% for c in item.candidates %} <button onclick="castVote({{ item.position.id }}, {{ c.id }})">投票给{{ c.fullname }}</button> {% endfor %} {% endif %} <hr> {% endfor %}
3. 修复Position模型的participants方法
原方法中直接累加candidate.votes(关联对象集合)会报错,需调用Candidate的calculate_votes方法:
class Position(models.Model): # ... 保留原有字段 ... def participants(self): total_votes = 0 for candidate in self.candidates.all(): total_votes += candidate.calculate_votes() return total_votes
内容的提问来源于stack exchange,提问作者Oluwaseyi Obanibi
相关产品推荐
相关产品推荐

