On sérialise les modèles pour transmettre en JSON, on améliore le formulaire modal

This commit is contained in:
Tom Hubrecht 2021-09-16 16:43:27 +02:00
parent 8a91648bac
commit a621bb8197
8 changed files with 87 additions and 38 deletions

View file

@ -7,6 +7,7 @@ from django.template.loader import render_to_string
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from shared.auth import CONNECTION_METHODS from shared.auth import CONNECTION_METHODS
from shared.json import Serializer
from shared.utils import choices_length from shared.utils import choices_length
from .staticdefs import ( from .staticdefs import (
@ -86,7 +87,7 @@ class Election(models.Model):
ordering = ["-start_date", "-end_date"] ordering = ["-start_date", "-end_date"]
class Question(models.Model): class Question(Serializer, models.Model):
election = models.ForeignKey( election = models.ForeignKey(
Election, related_name="questions", on_delete=models.CASCADE Election, related_name="questions", on_delete=models.CASCADE
) )
@ -110,6 +111,8 @@ class Question(models.Model):
blank=True, blank=True,
) )
serializable_fields = ["text_en", "text_fr", "type"]
def is_form_valid(self, vote_form): def is_form_valid(self, vote_form):
validate_function = getattr(ValidateFunctions, VALIDATE_FUNCTIONS[self.type]) validate_function = getattr(ValidateFunctions, VALIDATE_FUNCTIONS[self.type])
return vote_form.is_valid() and validate_function(vote_form) return vote_form.is_valid() and validate_function(vote_form)
@ -154,7 +157,7 @@ class Question(models.Model):
ordering = ["id"] ordering = ["id"]
class Option(models.Model): class Option(Serializer, models.Model):
question = models.ForeignKey( question = models.ForeignKey(
Question, related_name="options", on_delete=models.CASCADE Question, related_name="options", on_delete=models.CASCADE
) )
@ -171,6 +174,8 @@ class Option(models.Model):
# For now, we store the amount of votes received after the election is tallied # For now, we store the amount of votes received after the election is tallied
nb_votes = models.PositiveSmallIntegerField(_("nombre de votes reçus"), default=0) nb_votes = models.PositiveSmallIntegerField(_("nombre de votes reçus"), default=0)
serializable_fields = ["text_fr", "text_en", "abbreviation"]
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
# On enlève les espaces et on passe tout en majuscules # On enlève les espaces et on passe tout en majuscules
self.abbreviation = "".join(self.abbreviation.upper().split()) self.abbreviation = "".join(self.abbreviation.upper().split())

View file

@ -9,7 +9,7 @@
</span> </span>
</a> </a>
<a class="tag is-info is-light is-outlined has-tooltip-primary mb-0 modal-button" data-tooltip="{% trans "Modifier" %}" data-post_url="{% url 'election.mod-option' o.pk %}" data-target="modal-option" data-o_en="{{ o.text_en }}" data-o_fr="{{ o.text_fr }}" data-abbr="{{ o.abbreviation }}" data-title="{% trans "Modifier l'option" %}" data-type="option" data-parent="o_{{ o.pk }}"> <a class="tag is-info is-light is-outlined has-tooltip-primary mb-0 modal-button" data-tooltip="{% trans "Modifier" %}" data-post_url="{% url 'election.mod-option' o.pk %}" data-target="modal-option" data-json='{{ o.to_json }}' data-title="{% trans "Modifier l'option" %}" data-type="option" data-parent="o_{{ o.pk }}">
<span class="icon"> <span class="icon">
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</span> </span>

View file

@ -21,7 +21,7 @@
</span> </span>
</a> </a>
<a class="tag is-outlined is-light is-info ml-1 modal-button" data-post_url="{% url 'election.mod-question' q.pk %}" data-target="modal-question" data-q_type="{{ q.type }}" data-q_en="{{ q.text_en }}" data-q_fr="{{ q.text_fr }}" data-title="{% trans "Modifier la question" %}" data-type="question" data-parent="q_{{ q.pk }}"> <a class="tag is-outlined is-light is-info ml-1 modal-button" data-post_url="{% url 'election.mod-question' q.pk %}" data-target="modal-question" data-json='{{ q.to_json }}' data-title="{% trans "Modifier la question" %}" data-parent="q_{{ q.pk }}">
<span class="icon-text"> <span class="icon-text">
<span class="icon"> <span class="icon">
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
@ -56,7 +56,7 @@
{# Rajout d'une option #} {# Rajout d'une option #}
{% if q.election.start_date > current_time %} {% if q.election.start_date > current_time %}
<div class="panel-block"> <div class="panel-block">
<button class="button modal-button is-primary is-outlined is-fullwidth option" data-post_url="{% url 'election.add-option' q.pk %}" data-target="modal-option" data-title="{% trans "Rajouter une option" %}" data-type="option" data-next="options_{{ q.pk }}"> <button class="button modal-button is-primary is-outlined is-fullwidth option" data-post_url="{% url 'election.add-option' q.pk %}" data-target="modal-option" data-title="{% trans "Rajouter une option" %}" data-json='{"text_fr": "", "text_en": "", "abbreviation": ""}' data-next="options_{{ q.pk }}">
<span class="icon"> <span class="icon">
<i class="fas fa-plus"></i> <i class="fas fa-plus"></i>
</span> </span>

View file

@ -10,14 +10,10 @@
f.dataset.next = b.dataset.next; f.dataset.next = b.dataset.next;
f.dataset.origin = b.dataset.parent f.dataset.origin = b.dataset.parent
if (b.dataset.type == 'question') { const d = JSON.parse(b.dataset.json);
_$('[name="text_fr"]', f, false).value = b.dataset.q_fr || '';
_$('[name="text_en"]', f, false).value = b.dataset.q_en || ''; for (const [k, v] of Object.entries(d)) {
_$('[name="type"]', f, false).value = b.dataset.q_type || 'assentiment'; _$(`[name='${k}']`, f, false).value = v;
} else if (b.dataset.type == 'option') {
_$('[name="text_fr"]', f, false).value = b.dataset.o_fr || '';
_$('[name="text_en"]', f, false).value = b.dataset.o_en || '';
_$('[name="abbreviation"]', f, false).value = b.dataset.abbr || '';
} }
}); });
} }
@ -279,7 +275,7 @@
<div class="columns is-centered" id="q_add"> <div class="columns is-centered" id="q_add">
<div class="column is-two-thirds"> <div class="column is-two-thirds">
<button class="button modal-button is-primary is-outlined is-fullwidth question" data-post_url="{% url 'election.add-question' election.pk %}" data-target="modal-question" data-title="{% trans "Rajouter une question" %}" data-next="questions" data-type="question"> <button class="button modal-button is-primary is-outlined is-fullwidth question" data-post_url="{% url 'election.add-question' election.pk %}" data-target="modal-question" data-title="{% trans "Rajouter une question" %}" data-next="questions" data-json='{"text_fr": "", "text_en": "", "type": "assentiment"}'>
<span class="icon"> <span class="icon">
<i class="fas fa-question"></i> <i class="fas fa-question"></i>
</span> </span>

View file

@ -19,7 +19,7 @@ from django.views.generic import (
View, View,
) )
from shared.json.views import JsonCreateView, JsonDeleteView, JsonUpdateView from shared.json import JsonCreateView, JsonDeleteView, JsonUpdateView
from shared.views import BackgroundUpdateView, TimeMixin from shared.views import BackgroundUpdateView, TimeMixin
from .forms import ( from .forms import (
@ -71,6 +71,23 @@ class ElectionCreateView(AdminOnlyMixin, SuccessMessageMixin, CreateView):
return super().form_valid(form) return super().form_valid(form)
class ElectionDeleteView(CreatorOnlyMixin, BackgroundUpdateView):
model = Election
pattern_name = "election.list"
def get_object(self, queryset=None):
obj = self.get_object()
# On ne peut supprimer que les élections n'ayant pas eu de vote et dont
# le mail d'annonce n'a pas été fait
if obj.voters.exists() or obj.send_election_mail:
raise Http404
return obj
def get(self, request, *args, **kwargs):
self.get_object().delete()
return super().get(request, *args, **kwargs)
class ElectionAdminView(CreatorOnlyMixin, TimeMixin, DetailView): class ElectionAdminView(CreatorOnlyMixin, TimeMixin, DetailView):
model = Election model = Election
template_name = "elections/election_admin.html" template_name = "elections/election_admin.html"

10
shared/json/__init__.py Normal file
View file

@ -0,0 +1,10 @@
from .mixins import Serializer # noqa
from .views import JsonCreateView, JsonDeleteView, JsonDetailView, JsonUpdateView
__all__ = [
"Serializer",
"JsonCreateView",
"JsonDeleteView",
"JsonDetailView",
"JsonUpdateView",
]

21
shared/json/mixins.py Normal file
View file

@ -0,0 +1,21 @@
import json
class Serializer:
serializable_fields = []
def get_serializable_fields(self):
return self.serializable_fields
def to_json(self):
data = {}
for field in self.get_serializable_fields():
if hasattr(self, field):
data.update({field: getattr(self, field)})
else:
raise AttributeError(
"This object does not have a field named '{}'".format(field)
)
return json.dumps(data)

View file

@ -3,33 +3,33 @@
<div class="modal" id="modal-{{ modal_id }}"> <div class="modal" id="modal-{{ modal_id }}">
<div class="modal-background" data-closes="modal-{{ modal_id }}"></div> <div class="modal-background" data-closes="modal-{{ modal_id }}"></div>
<div class="modal-card"> <div class="modal-card">
<form method="post" action="{{ post_url }}" data-modal="modal-{{ modal_id }}">
{% csrf_token %}
<header class="modal-card-head"> <header class="modal-card-head">
<p class="modal-card-title">{{ modal_title }}</p> <p class="modal-card-title">{{ modal_title }}</p>
<a class="delete" aria-label="close" data-closes="modal-{{ modal_id }}"></a> <button class="delete" aria-label="close" data-closes="modal-{{ modal_id }}"></button>
</header> </header>
<section class="modal-card-body"> <section class="modal-card-body">
<form method="post" action="{{ post_url }}" id="form-{{ modal_id }}" data-modal="modal-{{ modal_id }}">
{% csrf_token %}
{% include "forms/form.html" %} {% include "forms/form.html" %}
</form>
</section> </section>
<footer class="modal-card-foot"> <footer class="modal-card-foot">
<button class="button is-fullwidth is-outlined is-primary is-light" type="submit"> <button class="button is-fullwidth is-outlined is-primary is-light" form="form-{{ modal_id }}" type="submit">
<span class="icon"> <span class="icon">
<i class="fas fa-check"></i> <i class="fas fa-check"></i>
</span> </span>
<span>{% trans "Enregistrer" %}</span> <span>{% trans "Enregistrer" %}</span>
</button> </button>
<a class="button is-primary button-close" data-closes="modal-{{ modal_id }}"> <button class="button is-primary button-close" data-closes="modal-{{ modal_id }}">
<span class="icon"> <span class="icon">
<i class="fas fa-times"></i> <i class="fas fa-times"></i>
</span> </span>
<span>{% trans "Annuler" %}</span> <span>{% trans "Annuler" %}</span>
</a> </button>
</footer> </footer>
</form>
</div> </div>
</div> </div>