manager: reduce number of queries when listing events (#38699)

This commit is contained in:
Frédéric Péters 2019-12-24 10:14:55 +01:00
parent 61496b5cdf
commit 6988b225c9
5 changed files with 84 additions and 12 deletions

View File

@ -20,11 +20,12 @@ import math
import requests
import vobject
import django
from django.conf import settings
from django.contrib.auth.models import Group
from django.core.exceptions import ValidationError
from django.db import models, transaction
from django.db.models import Q
from django.db.models import Count, Q, Case, When
from django.urls import reverse
from django.utils.dates import WEEKDAYS
from django.utils.encoding import force_text
@ -358,6 +359,41 @@ class Event(models.Model):
return False
return True
@staticmethod
def annotate_queryset(qs):
if django.VERSION < (2, 0):
return qs.annotate(
booked_places_count=Count(
Case(
When(
booking__cancellation_datetime__isnull=True,
booking__in_waiting_list=False,
then='booking',
)
)
),
waiting_list_count=Count(
Case(
When(
booking__cancellation_datetime__isnull=True,
booking__in_waiting_list=True,
then='booking',
)
)
),
)
else:
return qs.annotate(
booked_places_count=Count(
'booking',
filter=Q(booking__cancellation_datetime__isnull=True, booking__in_waiting_list=False),
),
waiting_list_count=Count(
'booking',
filter=Q(booking__cancellation_datetime__isnull=True, booking__in_waiting_list=True),
),
)
@property
def booked_places(self):
return self.booking_set.filter(cancellation_datetime__isnull=True, in_waiting_list=False).count()

View File

@ -8,13 +8,13 @@
{% if object_list %}
<ul class="objects-list single-links">
{% for event in object_list %}
<li class="{% if event.booked_places > event.places %}overbooking{% endif %}
<li class="{% if event.booked_places_count > event.places %}overbooking{% endif %}
{% if event.full %}full{% endif %}
{% if not event.in_bookable_period %}not-{% endif %}bookable"
{% if event.places %}
data-total="{{event.places}}" data-booked="{{event.booked_places}}"
data-total="{{event.places}}" data-booked="{{event.booked_places_count}}"
{% elif event.waiting_list_places %}
data-total="{{event.waiting_list_places}}" data-booked="{{event.waiting_list}}"
data-total="{{event.waiting_list_places}}" data-booked="{{event.waiting_list_count}}"
{% endif %}
><a href="{% url 'chrono-manager-event-view' pk=agenda.id event_pk=event.id %}">
{% if event.label %}{{event.label}} / {% endif %}
@ -22,11 +22,11 @@
{% if event.full %}/ <span class="full">{% trans "full" %}</span>{% endif %}
(
{% if event.places %}
{% blocktrans with places=event.places booked_places=event.booked_places %}{{ places }} places, {{ booked_places }} booked places{% endblocktrans %}
{% blocktrans with places=event.places booked_places=event.booked_places_count %}{{ places }} places, {{ booked_places }} booked places{% endblocktrans %}
{% endif %}
{% if event.places and event.waiting_list_places %} / {% endif %}
{% if event.waiting_list_places %}
{% blocktrans with places=event.waiting_list_places waiting_places=event.waiting_list %}
{% blocktrans with places=event.waiting_list_places waiting_places=event.waiting_list_count %}
{{waiting_places}} on {{ places }} in waiting list
{% endblocktrans %}
{% endif %}

View File

@ -13,14 +13,14 @@
<div>
{% if object.event_set.count %}
<ul class="objects-list single-links">
{% for event in object.event_set.all %}
<li class="{% if event.booked_places > event.places %}overbooking{% endif %}
{% for event in view.get_events %}
<li class="{% if event.booked_places_count > event.places %}overbooking{% endif %}
{% if event.full %}full{% endif %}
{% if not event.in_bookable_period %}not-{% endif %}bookable"
{% if event.places %}
data-total="{{event.places}}" data-booked="{{event.booked_places}}"
data-total="{{event.places}}" data-booked="{{event.booked_places_count}}"
{% elif event.waiting_list_places %}
data-total="{{event.waiting_list_places}}" data-booked="{{event.waiting_list}}"
data-total="{{event.waiting_list_places}}" data-booked="{{event.waiting_list_count}}"
{% endif %}
><a rel="popup" href="{% url 'chrono-manager-event-edit' pk=agenda.id event_pk=event.id %}?next=settings">
{% if event.label %}{{event.label}} / {% endif %}
@ -28,11 +28,11 @@
{% if event.full %}/ <span class="full">{% trans "full" %}</span>{% endif %}
(
{% if event.places %}
{% blocktrans with places=event.places booked_places=event.booked_places %}{{ places }} places, {{ booked_places }} booked places{% endblocktrans %}
{% blocktrans with places=event.places booked_places=event.booked_places_count %}{{ places }} places, {{ booked_places }} booked places{% endblocktrans %}
{% endif %}
{% if event.places and event.waiting_list_places %} / {% endif %}
{% if event.waiting_list_places %}
{% blocktrans with places=event.waiting_list_places waiting_places=event.waiting_list %}
{% blocktrans with places=event.waiting_list_places waiting_places=event.waiting_list_count %}
{{waiting_places}} on {{ places }} in waiting list
{% endblocktrans %}
{% endif %}

View File

@ -420,6 +420,12 @@ agenda_day_view = AgendaDayView.as_view()
class AgendaMonthView(AgendaDateView, MonthArchiveView):
def get_queryset(self):
qs = super(AgendaMonthView, self).get_queryset()
if self.agenda.kind == 'meetings':
return qs
return Event.annotate_queryset(qs)
def get_template_names(self):
return ['chrono/manager_%s_agenda_month_view.html' % self.agenda.kind]
@ -621,6 +627,9 @@ class ManagedDeskSubobjectMixin(object):
class AgendaSettings(ManagedAgendaMixin, DetailView):
model = Agenda
def get_events(self):
return Event.annotate_queryset(Event.objects.filter(agenda=self.agenda).select_related('agenda'))
def get_template_names(self):
return ['chrono/manager_%s_agenda_settings.html' % self.agenda.kind]

View File

@ -455,3 +455,30 @@ def test_management_role_deletion():
Agenda.objects.get(id=agenda.id).view_role is None
Agenda.objects.get(id=agenda.id).edit_role is None
def test_event_bookings_annotation():
agenda = Agenda(label='test', kind='events')
agenda.save()
event = Event(start_datetime=now(), label='foo', places=10, waiting_list_places=10, agenda=agenda)
event.save()
event2 = Event(start_datetime=now(), label='bar', places=10, waiting_list_places=10, agenda=agenda)
event2.save()
Booking(event=event).save()
Booking(event=event).save()
Booking(event=event, cancellation_datetime=now()).save()
Booking(event=event, in_waiting_list=True).save()
Booking(event=event, in_waiting_list=True, cancellation_datetime=now()).save()
Booking(event=event2).save()
Booking(event=event2).save()
Booking(event=event2).save()
for event in Event.annotate_queryset(Event.objects.filter(agenda=agenda)):
if event.label == 'foo':
assert event.booked_places_count == 2
assert event.waiting_list_count == 1
elif event.label == 'bar':
assert event.booked_places_count == 3
assert event.waiting_list_count == 0