On sérialise les modèles pour transmettre en JSON, on améliore le formulaire modal
This commit is contained in:
parent
8a91648bac
commit
a621bb8197
8 changed files with 87 additions and 38 deletions
|
@ -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())
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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
10
shared/json/__init__.py
Normal 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
21
shared/json/mixins.py
Normal 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)
|
|
@ -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" %}
|
||||||
</section>
|
</form>
|
||||||
|
</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>
|
||||||
|
|
Loading…
Reference in a new issue