From 3d8a225ed193f356de9ce4083865ee23337ffae2 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Fri, 18 Dec 2020 00:19:18 +0100 Subject: [PATCH] =?UTF-8?q?D=C3=A9but=20du=20vote?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- elections/forms.py | 31 ++++++++++++ .../templates/elections/election_create.html | 6 +-- elections/templates/elections/vote.html | 23 +++++++++ elections/urls.py | 2 + elections/views.py | 47 +++++++++++++++++-- 5 files changed, 101 insertions(+), 8 deletions(-) create mode 100644 elections/forms.py create mode 100644 elections/templates/elections/vote.html diff --git a/elections/forms.py b/elections/forms.py new file mode 100644 index 0000000..f05237c --- /dev/null +++ b/elections/forms.py @@ -0,0 +1,31 @@ +from django import forms +from django.forms.models import inlineformset_factory + +from .models import Option, Question + + +class VoteForm(forms.ModelForm): + def __init__(self, **kwargs): + super().__init__(**kwargs) + # We set the option's text as the label for the checkbox + instance = kwargs.get("instance", None) + if instance is not None: + self.fields["selected"].label = instance.text + + selected = forms.BooleanField(required=False) + + def record_vote(self, user): + self.full_clean() + if self.cleaned_data["selected"]: + self.instance.voters.add(user) + else: + self.instance.voters.remove(user) + + class Meta: + model = Option + fields = [] + + +OptionFormSet = inlineformset_factory( + Question, Option, extra=0, form=VoteForm, can_delete=False +) diff --git a/elections/templates/elections/election_create.html b/elections/templates/elections/election_create.html index 106ce12..0363536 100644 --- a/elections/templates/elections/election_create.html +++ b/elections/templates/elections/election_create.html @@ -6,7 +6,7 @@ {% for error in form.non_field_errors %}
- {{ error }} + {{ error }}
{% endfor %} @@ -15,12 +15,12 @@
{% csrf_token %} - + {% include "elections/forms/form.html" with form=form errors=False %}

- +

diff --git a/elections/templates/elections/vote.html b/elections/templates/elections/vote.html new file mode 100644 index 0000000..f27f786 --- /dev/null +++ b/elections/templates/elections/vote.html @@ -0,0 +1,23 @@ +{% extends "elections/base.html" %} +{% load i18n %} + + +{% block content %} + +

{% trans "Vote pour la question :" %} {{ question }}

+ +
+
+ {% csrf_token %} + + {{ vote_form }} + +
+

+ +

+
+
+
+ +{% endblock %} diff --git a/elections/urls.py b/elections/urls.py index a6a8a7d..7b179a9 100644 --- a/elections/urls.py +++ b/elections/urls.py @@ -1,8 +1,10 @@ from django.urls import path + from . import views urlpatterns = [ path("create/", views.ElectionCreateView.as_view(), name="election.create"), path("update/", views.ElectionUpdateView.as_view(), name="election.update"), path("view/", views.ElectionView.as_view(), name="election.view"), + path("vote/", views.VoteView.as_view(), name="election.vote"), ] diff --git a/elections/views.py b/elections/views.py index f580e62..99228bf 100644 --- a/elections/views.py +++ b/elections/views.py @@ -1,9 +1,12 @@ -from django.views.generic import CreateView, UpdateView, DetailView from django.contrib.messages.views import SuccessMessageMixin -from django.utils.translation import gettext_lazy as _ from django.db.models import Count, Prefetch +from django.http import HttpResponseRedirect +from django.urls import reverse +from django.utils.translation import gettext_lazy as _ +from django.views.generic import CreateView, DetailView, UpdateView -from .models import Election, Question, Option +from .forms import OptionFormSet +from .models import Election, Option, Question # TODO: access control *everywhere* @@ -27,10 +30,11 @@ class ElectionUpdateView(SuccessMessageMixin, UpdateView): class ElectionView(DetailView): model = Election + template_name = "elections/election.html" def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - if self.election.tallied: + if self.object.tallied: options_qs = Option.objects.annotate(nb_votes=Count("voters")) questions = self.election.question.prefetch_related( Prefetch("options", queryset=options_qs) @@ -40,4 +44,37 @@ class ElectionView(DetailView): return context def get_queryset(self): - return super().get_queryset().filter(archived=False).select_related("questions") + return super().get_queryset().filter(archived=False) + + +class VoteView(DetailView): + model = Question + template_name = "elections/vote.html" + + def get_queryset(self): + # On ne peut voter que si l'élection n'a pas été comptée + return ( + super() + .get_queryset() + .filter(election__tallied=False, election__archived=False) + .select_related("election") + ) + + def get(self, request, *args, **kwargs): + self.object = self.get_object() + vote_form = OptionFormSet(instance=self.object) + + return self.render_to_response(self.get_context_data(vote_form=vote_form)) + + def post(self, request, *args, **kwargs): + self.object = self.get_object() + vote_form = OptionFormSet(self.request.POST, instance=self.object) + + # We record the cast votes + for v in vote_form: + v.record_vote(self.request.user) + + return HttpResponseRedirect( + reverse("election.view", args=[self.object.election.pk]) + + f"#q_{self.object.pk}" + )