From e704e2a155ffd3ccd0667f0a5d9da6c26f367af2 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Sun, 20 Dec 2020 18:50:38 +0100 Subject: [PATCH] =?UTF-8?q?On=20restreint=20l'acc=C3=A8s=20au=20vote?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- elections/forms.py | 2 +- .../migrations/0002_auto_20201220_1735.py | 36 +++++++++++++++ .../migrations/0003_election_restricted.py | 20 +++++++++ .../migrations/0004_auto_20201220_1847.py | 25 +++++++++++ elections/mixins.py | 2 +- elections/models.py | 45 ++++++++++++++----- elections/views.py | 22 ++++----- 7 files changed, 128 insertions(+), 24 deletions(-) create mode 100644 elections/migrations/0002_auto_20201220_1735.py create mode 100644 elections/migrations/0003_election_restricted.py create mode 100644 elections/migrations/0004_auto_20201220_1847.py diff --git a/elections/forms.py b/elections/forms.py index ed2093c..bb04956 100644 --- a/elections/forms.py +++ b/elections/forms.py @@ -21,7 +21,7 @@ class ElectionForm(forms.ModelForm): class Meta: model = Election - fields = ["name", "description", "start_date", "end_date"] + fields = ["name", "description", "restricted", "start_date", "end_date"] class QuestionForm(forms.ModelForm): diff --git a/elections/migrations/0002_auto_20201220_1735.py b/elections/migrations/0002_auto_20201220_1735.py new file mode 100644 index 0000000..b73e907 --- /dev/null +++ b/elections/migrations/0002_auto_20201220_1735.py @@ -0,0 +1,36 @@ +# Generated by Django 2.2.17 on 2020-12-20 16:35 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("elections", "0001_initial"), + ] + + operations = [ + migrations.AddField( + model_name="user", + name="election", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="registered_voters", + to="elections.Election", + ), + ), + migrations.AlterField( + model_name="election", + name="created_by", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="elections_created", + to=settings.AUTH_USER_MODEL, + ), + ), + ] diff --git a/elections/migrations/0003_election_restricted.py b/elections/migrations/0003_election_restricted.py new file mode 100644 index 0000000..9f4372a --- /dev/null +++ b/elections/migrations/0003_election_restricted.py @@ -0,0 +1,20 @@ +# Generated by Django 2.2.17 on 2020-12-20 17:20 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("elections", "0002_auto_20201220_1735"), + ] + + operations = [ + migrations.AddField( + model_name="election", + name="restricted", + field=models.BooleanField( + default=True, verbose_name="restreint le vote à une liste de personnes" + ), + ), + ] diff --git a/elections/migrations/0004_auto_20201220_1847.py b/elections/migrations/0004_auto_20201220_1847.py new file mode 100644 index 0000000..549faf0 --- /dev/null +++ b/elections/migrations/0004_auto_20201220_1847.py @@ -0,0 +1,25 @@ +# Generated by Django 2.2.17 on 2020-12-20 17:47 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("elections", "0003_election_restricted"), + ] + + operations = [ + migrations.AlterField( + model_name="user", + name="election", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="registered_voters", + to="elections.Election", + ), + ), + ] diff --git a/elections/mixins.py b/elections/mixins.py index 314d923..cd3d15a 100644 --- a/elections/mixins.py +++ b/elections/mixins.py @@ -35,7 +35,7 @@ class RestrictAccessMixin(SelectElectionMixin): return qs.none() -class OpenElectionOnly(RestrictAccessMixin): +class OpenElectionOnlyMixin(RestrictAccessMixin): """N'autorise la vue que lorsque l'élection est ouverte""" def get_filters(self): diff --git a/elections/models.py b/elections/models.py index c6a7d17..e99a4d2 100644 --- a/elections/models.py +++ b/elections/models.py @@ -1,16 +1,8 @@ +from django.conf import settings from django.contrib.auth.models import AbstractUser from django.db import models from django.utils.translation import gettext_lazy as _ -# ############################################################################# -# Modification of the base User Model -# ############################################################################# - - -class User(AbstractUser): - pass - - # ############################################################################# # Models regarding an election # ############################################################################# @@ -24,8 +16,16 @@ class Election(models.Model): start_date = models.DateTimeField(_("date et heure de début")) end_date = models.DateTimeField(_("date et heure de fin")) + restricted = models.BooleanField( + _("restreint le vote à une liste de personnes"), default=True + ) + created_by = models.ForeignKey( - User, on_delete=models.SET_NULL, blank=True, null=True + settings.AUTH_USER_MODEL, + related_name="elections_created", + on_delete=models.SET_NULL, + blank=True, + null=True, ) results_public = models.BooleanField(_("résultats publics"), default=False) @@ -58,7 +58,7 @@ class Option(models.Model): ) text = models.TextField(_("texte"), blank=False) voters = models.ManyToManyField( - User, + settings.AUTH_USER_MODEL, related_name="votes", ) # For now, we store the amount of votes received after the election is tallied @@ -66,3 +66,26 @@ class Option(models.Model): class Meta: ordering = ["id"] + + +# ############################################################################# +# Modification of the base User Model +# ############################################################################# + + +class User(AbstractUser): + election = models.ForeignKey( + Election, + related_name="registered_voters", + null=True, + blank=True, + on_delete=models.CASCADE, + ) + + def can_vote(self, election): + # Si c'est un·e utilisateur·ice CAS, iel peut voter dans les élections + # ouvertes à tou·te·s + if self.election is None: + return not election.restricted + # Pour les élections restreintes, il faut y être associé + return election.restricted and (self.election == election) diff --git a/elections/views.py b/elections/views.py index a960237..f43355b 100644 --- a/elections/views.py +++ b/elections/views.py @@ -2,7 +2,7 @@ from django.contrib import messages from django.contrib.messages.views import SuccessMessageMixin # from django.db.models import Count, Prefetch -from django.http import HttpResponseRedirect +from django.http import Http404, HttpResponseRedirect from django.urls import reverse from django.utils import timezone from django.utils.text import slugify @@ -16,7 +16,7 @@ from django.views.generic import ( ) from .forms import ElectionForm, OptionForm, OptionFormSet, QuestionForm -from .mixins import CreatorOnlyEditMixin, CreatorOnlyMixin, OpenElectionOnly +from .mixins import CreatorOnlyEditMixin, CreatorOnlyMixin, OpenElectionOnlyMixin from .models import Election, Option, Question # TODO: access control *everywhere* @@ -268,14 +268,6 @@ class ElectionView(DetailView): def get_context_data(self, **kwargs): kwargs.update({"current_time": timezone.now()}) return super().get_context_data(**kwargs) - # context = super().get_context_data(**kwargs) - # if self.object.tallied: - # options_qs = Option.objects.annotate(nb_votes=Count("voters")) - # questions = self.election.question.prefetch_related( - # Prefetch("options", queryset=options_qs) - # ) - # context["questions"] = questions - # return context def get_queryset(self): return ( @@ -286,7 +278,7 @@ class ElectionView(DetailView): ) -class VoteView(OpenElectionOnly, DetailView): +class VoteView(OpenElectionOnlyMixin, DetailView): model = Question template_name = "elections/vote.html" @@ -302,6 +294,14 @@ class VoteView(OpenElectionOnly, DetailView): return reverse("election.vote", args=[q_next]) + def get_object(self): + question = super().get_object() + # Seulement les utilisateur·ice·s ayant le droit de voter dans l'élection + # peuvent voir la page + if not self.request.user.can_vote(question.election): + raise Http404 + return question + def get(self, request, *args, **kwargs): self.object = self.get_object() vote_form = OptionFormSet(instance=self.object)