forked from DGNum/gestioCOF
Fewer db requests on bda views.
bda.views.etat_places - Use select_related on spectacles_set, avoid query issue for each spectacle. - `slots__sum` is computed in view, instead of a query. - Cleaner code (and avoid useless computations). bda.views.places - Add select_related on places, avoid query issue for each spectacle. bda.views.inscription - 1 query for spectacle field choices, instead of (#forms in formset * #spectacles) - Delete BaseBdaFormSet. The validation was redundant with `unique_together` of ChoixSpectacle model.
This commit is contained in:
parent
b8aa5d8bbe
commit
3e0bd2e758
2 changed files with 98 additions and 77 deletions
23
bda/forms.py
23
bda/forms.py
|
@ -5,33 +5,10 @@ from __future__ import print_function
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.forms.models import BaseInlineFormSet
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from bda.models import Attribution, Spectacle
|
from bda.models import Attribution, Spectacle
|
||||||
|
|
||||||
|
|
||||||
class BaseBdaFormSet(BaseInlineFormSet):
|
|
||||||
def clean(self):
|
|
||||||
"""Checks that no two articles have the same title."""
|
|
||||||
super(BaseBdaFormSet, self).clean()
|
|
||||||
if any(self.errors):
|
|
||||||
# Don't bother validating the formset unless each form is valid on
|
|
||||||
# its own
|
|
||||||
return
|
|
||||||
spectacles = []
|
|
||||||
for i in range(0, self.total_form_count()):
|
|
||||||
form = self.forms[i]
|
|
||||||
if not form.cleaned_data:
|
|
||||||
continue
|
|
||||||
spectacle = form.cleaned_data['spectacle']
|
|
||||||
delete = form.cleaned_data['DELETE']
|
|
||||||
if not delete and spectacle in spectacles:
|
|
||||||
raise forms.ValidationError(
|
|
||||||
"Vous ne pouvez pas vous inscrire deux fois pour le "
|
|
||||||
"même spectacle.")
|
|
||||||
spectacles.append(spectacle)
|
|
||||||
|
|
||||||
|
|
||||||
class TokenForm(forms.Form):
|
class TokenForm(forms.Form):
|
||||||
token = forms.CharField(widget=forms.widgets.Textarea())
|
token = forms.CharField(widget=forms.widgets.Textarea())
|
||||||
|
|
||||||
|
|
152
bda/views.py
152
bda/views.py
|
@ -1,5 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from functools import partial
|
||||||
import random
|
import random
|
||||||
import hashlib
|
import hashlib
|
||||||
import time
|
import time
|
||||||
|
@ -11,9 +12,9 @@ from custommail.shortcuts import (
|
||||||
from django.shortcuts import render, get_object_or_404
|
from django.shortcuts import render, get_object_or_404
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.db import models, transaction
|
from django.db import transaction
|
||||||
from django.core import serializers
|
from django.core import serializers
|
||||||
from django.db.models import Count, Q, Sum
|
from django.db.models import Count, Q
|
||||||
from django.forms.models import inlineformset_factory
|
from django.forms.models import inlineformset_factory
|
||||||
from django.http import (
|
from django.http import (
|
||||||
HttpResponseBadRequest, HttpResponseRedirect, JsonResponse
|
HttpResponseBadRequest, HttpResponseRedirect, JsonResponse
|
||||||
|
@ -29,8 +30,7 @@ from bda.models import (
|
||||||
)
|
)
|
||||||
from bda.algorithm import Algorithm
|
from bda.algorithm import Algorithm
|
||||||
from bda.forms import (
|
from bda.forms import (
|
||||||
BaseBdaFormSet, TokenForm, ResellForm, AnnulForm, InscriptionReventeForm,
|
TokenForm, ResellForm, AnnulForm, InscriptionReventeForm, SoldForm,
|
||||||
SoldForm
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -44,39 +44,44 @@ def etat_places(request, tirage_id):
|
||||||
Et le total de toutes les demandes
|
Et le total de toutes les demandes
|
||||||
"""
|
"""
|
||||||
tirage = get_object_or_404(Tirage, id=tirage_id)
|
tirage = get_object_or_404(Tirage, id=tirage_id)
|
||||||
spectacles1 = ChoixSpectacle.objects \
|
|
||||||
.filter(spectacle__tirage=tirage) \
|
spectacles = tirage.spectacle_set.select_related('location')
|
||||||
.filter(double_choice="1") \
|
spectacles_dict = {} # index of spectacle by id
|
||||||
.all() \
|
|
||||||
.values('spectacle', 'spectacle__title') \
|
|
||||||
.annotate(total=models.Count('spectacle'))
|
|
||||||
spectacles2 = ChoixSpectacle.objects \
|
|
||||||
.filter(spectacle__tirage=tirage) \
|
|
||||||
.exclude(double_choice="1") \
|
|
||||||
.all() \
|
|
||||||
.values('spectacle', 'spectacle__title') \
|
|
||||||
.annotate(total=models.Count('spectacle'))
|
|
||||||
spectacles = tirage.spectacle_set.all()
|
|
||||||
spectacles_dict = {}
|
|
||||||
total = 0
|
|
||||||
for spectacle in spectacles:
|
for spectacle in spectacles:
|
||||||
spectacle.total = 0
|
spectacle.total = 0 # init total requests
|
||||||
spectacle.ratio = 0.0
|
|
||||||
spectacles_dict[spectacle.id] = spectacle
|
spectacles_dict[spectacle.id] = spectacle
|
||||||
for spectacle in spectacles1:
|
|
||||||
spectacles_dict[spectacle["spectacle"]].total += spectacle["total"]
|
choices = (
|
||||||
spectacles_dict[spectacle["spectacle"]].ratio = \
|
ChoixSpectacle.objects
|
||||||
spectacles_dict[spectacle["spectacle"]].total / \
|
.filter(spectacle__in=spectacles)
|
||||||
spectacles_dict[spectacle["spectacle"]].slots
|
.values('spectacle')
|
||||||
total += spectacle["total"]
|
.annotate(total=Count('spectacle'))
|
||||||
for spectacle in spectacles2:
|
)
|
||||||
spectacles_dict[spectacle["spectacle"]].total += 2*spectacle["total"]
|
|
||||||
spectacles_dict[spectacle["spectacle"]].ratio = \
|
# choices *by spectacles* whose only 1 place is requested
|
||||||
spectacles_dict[spectacle["spectacle"]].total / \
|
choices1 = choices.filter(double_choice="1")
|
||||||
spectacles_dict[spectacle["spectacle"]].slots
|
# choices *by spectacles* whose 2 places is requested
|
||||||
total += 2*spectacle["total"]
|
choices2 = choices.exclude(double_choice="1")
|
||||||
|
|
||||||
|
for spectacle in choices1:
|
||||||
|
pk = spectacle['spectacle']
|
||||||
|
spectacles_dict[pk].total += spectacle['total']
|
||||||
|
for spectacle in choices2:
|
||||||
|
pk = spectacle['spectacle']
|
||||||
|
spectacles_dict[pk].total += 2*spectacle['total']
|
||||||
|
|
||||||
|
# here, each spectacle.total contains the number of requests
|
||||||
|
|
||||||
|
slots = 0 # proposed slots
|
||||||
|
total = 0 # requests
|
||||||
|
for spectacle in spectacles:
|
||||||
|
slots += spectacle.slots
|
||||||
|
total += spectacle.total
|
||||||
|
spectacle.ratio = spectacle.total / spectacle.slots
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
"proposed": tirage.spectacle_set.aggregate(Sum('slots'))['slots__sum'],
|
"proposed": slots,
|
||||||
"spectacles": spectacles,
|
"spectacles": spectacles,
|
||||||
"total": total,
|
"total": total,
|
||||||
'tirage': tirage
|
'tirage': tirage
|
||||||
|
@ -94,11 +99,16 @@ def _hash_queryset(queryset):
|
||||||
@cof_required
|
@cof_required
|
||||||
def places(request, tirage_id):
|
def places(request, tirage_id):
|
||||||
tirage = get_object_or_404(Tirage, id=tirage_id)
|
tirage = get_object_or_404(Tirage, id=tirage_id)
|
||||||
participant, created = Participant.objects.get_or_create(
|
participant, _ = (
|
||||||
user=request.user, tirage=tirage)
|
Participant.objects
|
||||||
places = participant.attribution_set.order_by(
|
.get_or_create(user=request.user, tirage=tirage)
|
||||||
"spectacle__date", "spectacle").all()
|
)
|
||||||
total = sum([place.spectacle.price for place in places])
|
places = (
|
||||||
|
participant.attribution_set
|
||||||
|
.order_by("spectacle__date", "spectacle")
|
||||||
|
.select_related("spectacle", "spectacle__location")
|
||||||
|
)
|
||||||
|
total = sum(place.spectacle.price for place in places)
|
||||||
filtered_places = []
|
filtered_places = []
|
||||||
places_dict = {}
|
places_dict = {}
|
||||||
spectacles = []
|
spectacles = []
|
||||||
|
@ -148,33 +158,62 @@ def inscription(request, tirage_id):
|
||||||
return render(request, 'bda/resume-inscription-tirage.html', {})
|
return render(request, 'bda/resume-inscription-tirage.html', {})
|
||||||
if timezone.now() > tirage.fermeture:
|
if timezone.now() > tirage.fermeture:
|
||||||
# Le tirage est fermé.
|
# Le tirage est fermé.
|
||||||
participant, created = Participant.objects.get_or_create(
|
participant, _ = (
|
||||||
user=request.user, tirage=tirage)
|
Participant.objects
|
||||||
choices = participant.choixspectacle_set.order_by("priority").all()
|
.get_or_create(user=request.user, tirage=tirage)
|
||||||
|
)
|
||||||
|
choices = participant.choixspectacle_set.order_by("priority")
|
||||||
messages.error(request,
|
messages.error(request,
|
||||||
" C'est fini : tirage au sort dans la journée !")
|
" C'est fini : tirage au sort dans la journée !")
|
||||||
return render(request, "bda/resume-inscription-tirage.html",
|
return render(request, "bda/resume-inscription-tirage.html",
|
||||||
{"choices": choices})
|
{"choices": choices})
|
||||||
|
|
||||||
def formfield_callback(f, **kwargs):
|
def force_for(f, to_choices=None, **kwargs):
|
||||||
|
"""Overrides choices for ModelChoiceField.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
f (models.Field): To render as forms.Field
|
||||||
|
to_choices (dict): If a key `f.name` exists, f._choices is set to
|
||||||
|
its value.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Fonction utilisée par inlineformset_factory ci dessous.
|
formfield = f.formfield(**kwargs)
|
||||||
Restreint les spectacles proposés aux spectacles du bo tirage.
|
if to_choices:
|
||||||
"""
|
if f.name in to_choices:
|
||||||
if f.name == "spectacle":
|
choices = [('', '---------')] + to_choices[f.name]
|
||||||
kwargs['queryset'] = tirage.spectacle_set
|
formfield._choices = choices
|
||||||
return f.formfield(**kwargs)
|
return formfield
|
||||||
|
|
||||||
|
# Restrict spectacles choices to spectacles for this tirage.
|
||||||
|
spectacles = (
|
||||||
|
tirage.spectacle_set
|
||||||
|
.select_related('location')
|
||||||
|
)
|
||||||
|
spectacles_field_choices = [(sp.pk, str(sp)) for sp in spectacles]
|
||||||
|
|
||||||
|
# Allow for spectacle choices to be set once for all.
|
||||||
|
# Form display use 1 request instead of (#forms of formset * #spectacles).
|
||||||
|
# FIXME: Validation still generates too much requests...
|
||||||
|
formfield_callback = partial(
|
||||||
|
force_for,
|
||||||
|
to_choices={
|
||||||
|
'spectacle': spectacles_field_choices,
|
||||||
|
},
|
||||||
|
)
|
||||||
BdaFormSet = inlineformset_factory(
|
BdaFormSet = inlineformset_factory(
|
||||||
Participant,
|
Participant,
|
||||||
ChoixSpectacle,
|
ChoixSpectacle,
|
||||||
fields=("spectacle", "double_choice", "priority"),
|
fields=("spectacle", "double_choice", "priority"),
|
||||||
formset=BaseBdaFormSet,
|
formfield_callback=formfield_callback,
|
||||||
formfield_callback=formfield_callback)
|
)
|
||||||
participant, created = Participant.objects.get_or_create(
|
participant, _ = (
|
||||||
user=request.user, tirage=tirage)
|
Participant.objects
|
||||||
|
.get_or_create(user=request.user, tirage=tirage)
|
||||||
|
)
|
||||||
success = False
|
success = False
|
||||||
stateerror = False
|
stateerror = False
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
|
# use *this* queryset
|
||||||
dbstate = _hash_queryset(participant.choixspectacle_set.all())
|
dbstate = _hash_queryset(participant.choixspectacle_set.all())
|
||||||
if "dbstate" in request.POST and dbstate != request.POST["dbstate"]:
|
if "dbstate" in request.POST and dbstate != request.POST["dbstate"]:
|
||||||
stateerror = True
|
stateerror = True
|
||||||
|
@ -187,9 +226,14 @@ def inscription(request, tirage_id):
|
||||||
formset = BdaFormSet(instance=participant)
|
formset = BdaFormSet(instance=participant)
|
||||||
else:
|
else:
|
||||||
formset = BdaFormSet(instance=participant)
|
formset = BdaFormSet(instance=participant)
|
||||||
|
# use *this* queryset
|
||||||
dbstate = _hash_queryset(participant.choixspectacle_set.all())
|
dbstate = _hash_queryset(participant.choixspectacle_set.all())
|
||||||
total_price = 0
|
total_price = 0
|
||||||
for choice in participant.choixspectacle_set.all():
|
choices = (
|
||||||
|
participant.choixspectacle_set
|
||||||
|
.select_related('spectacle')
|
||||||
|
)
|
||||||
|
for choice in choices:
|
||||||
total_price += choice.spectacle.price
|
total_price += choice.spectacle.price
|
||||||
if choice.double:
|
if choice.double:
|
||||||
total_price += choice.spectacle.price
|
total_price += choice.spectacle.price
|
||||||
|
|
Loading…
Reference in a new issue