From 3b56d2c22b448238f5a2fc38cb3d2c85c0d9e5ba Mon Sep 17 00:00:00 2001 From: imperosol Date: Fri, 7 Nov 2025 13:05:55 +0100 Subject: [PATCH 1/2] add index on `Selling.date` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit L'index prend ~20% de la taille de la table (la table fait un peu plus de 100%Mo, et l'index un peu plus de 20Mo), mais permet de diviser par 10 les requêtes qui filtrent sur la date des ventes. Ca concerne notamment les requêtes db faites sur les pages suivantes : - les appels à facture (~75ms → ~3ms) - les ventes d'un club (entre 300ms et 450ms → entre 10ms et 15ms) - le top conso des comptoirs (sur le Foyer, ~90ms → ~9ms) - les dernières opérations d'un comptoir (sur le Foyer, ~130ms → ~1.5ms J'aurais bien aimé mettre également un index sur la troncature au mois de la date, mais c'est compliqué à mettre en place étant donné que postgres ne prend des index que sur des expressions immuables et que, comme elle dépend de la timezone (qui peut changer), la fonction date_trunc n'est pas immuable. --- ...4_alter_selling_date_selling_date_month_idx.py | 15 +++++++++++++++ counter/models.py | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 counter/migrations/0034_alter_selling_date_selling_date_month_idx.py diff --git a/counter/migrations/0034_alter_selling_date_selling_date_month_idx.py b/counter/migrations/0034_alter_selling_date_selling_date_month_idx.py new file mode 100644 index 00000000..719b62de --- /dev/null +++ b/counter/migrations/0034_alter_selling_date_selling_date_month_idx.py @@ -0,0 +1,15 @@ +# Generated by Django 5.2.3 on 2025-11-05 08:47 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [("counter", "0033_invoicecall")] + + operations = [ + migrations.AlterField( + model_name="selling", + name="date", + field=models.DateTimeField(db_index=True, verbose_name="date"), + ), + ] diff --git a/counter/models.py b/counter/models.py index e08e3905..1e92612b 100644 --- a/counter/models.py +++ b/counter/models.py @@ -849,7 +849,7 @@ class Selling(models.Model): blank=False, on_delete=models.SET_NULL, ) - date = models.DateTimeField(_("date")) + date = models.DateTimeField(_("date"), db_index=True) payment_method = models.CharField( _("payment method"), max_length=255, From 742ac504dc42da54753569d8717df81d92cd515b Mon Sep 17 00:00:00 2001 From: imperosol Date: Fri, 7 Nov 2025 13:07:46 +0100 Subject: [PATCH 2/2] optimize db requests on club sales view --- club/templates/club/club_sellings.jinja | 15 ++++++++------ club/views.py | 26 ++++++++++++------------- core/templates/core/macros.jinja | 4 ++-- locale/fr/LC_MESSAGES/django.po | 26 +++++++------------------ 4 files changed, 31 insertions(+), 40 deletions(-) diff --git a/club/templates/club/club_sellings.jinja b/club/templates/club/club_sellings.jinja index 3733d0c8..59edd18e 100644 --- a/club/templates/club/club_sellings.jinja +++ b/club/templates/club/club_sellings.jinja @@ -6,11 +6,11 @@ because it works with a somewhat dynamic form, but was written before Alpine was introduced in the project. TODO : rewrite the pagination used in this template an Alpine one #} -{% macro paginate(page_obj, paginator, js_action) %} - {% set js = js_action|default('') %} +{% macro paginate(page_obj, paginator) %} + {% set js = "formPagination(this)" %} {% if page_obj.has_previous() or page_obj.has_next() %} {% if page_obj.has_previous() %} - {% trans %}Previous{% endtrans %} + {% trans %}Previous{% endtrans %} {% else %} {% trans %}Previous{% endtrans %} {% endif %} @@ -18,11 +18,11 @@ TODO : rewrite the pagination used in this template an Alpine one {% if page_obj.number == i %} {{ i }} ({% trans %}current{% endtrans %}) {% else %} - {{ i }} + {{ i }} {% endif %} {% endfor %} {% if page_obj.has_next() %} - {% trans %}Next{% endtrans %} + {% trans %}Next{% endtrans %} {% else %} {% trans %}Next{% endtrans %} {% endif %} @@ -81,6 +81,10 @@ TODO : rewrite the pagination used in this template an Alpine one {% endfor %} + {{ paginate(paginated_result, paginator) }} +{% endblock %} + +{% block script %} - {{ paginate(paginated_result, paginator, "formPagination(this)") }} {% endblock %} diff --git a/club/views.py b/club/views.py index 02e87ba7..103eecb4 100644 --- a/club/views.py +++ b/club/views.py @@ -30,7 +30,7 @@ from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.messages.views import SuccessMessageMixin from django.core.exceptions import NON_FIELD_ERRORS, PermissionDenied, ValidationError from django.core.paginator import InvalidPage, Paginator -from django.db.models import Q, Sum +from django.db.models import F, Q, Sum from django.http import Http404, HttpResponseRedirect, StreamingHttpResponse from django.shortcuts import get_object_or_404, redirect from django.urls import reverse, reverse_lazy @@ -370,7 +370,7 @@ class ClubOldMembersView(ClubTabsMixin, PermissionRequiredMixin, DetailView): class ClubSellingView(ClubTabsMixin, CanEditMixin, DetailFormView): - """Sellings of a club.""" + """Sales of a club.""" model = Club pk_url_kwarg = "club_id" @@ -396,9 +396,8 @@ class ClubSellingView(ClubTabsMixin, CanEditMixin, DetailFormView): def get_context_data(self, **kwargs): kwargs = super().get_context_data(**kwargs) - qs = Selling.objects.filter(club=self.object) - kwargs["result"] = qs[:0] + kwargs["result"] = Selling.objects.none() kwargs["paginated_result"] = kwargs["result"] kwargs["total"] = 0 kwargs["total_quantity"] = 0 @@ -406,6 +405,7 @@ class ClubSellingView(ClubTabsMixin, CanEditMixin, DetailFormView): form = self.get_form() if form.is_valid(): + qs = Selling.objects.filter(club=self.object) if not len([v for v in form.cleaned_data.values() if v is not None]): qs = Selling.objects.none() if form.cleaned_data["begin_date"]: @@ -425,18 +425,18 @@ class ClubSellingView(ClubTabsMixin, CanEditMixin, DetailFormView): if len(selected_products) > 0: qs = qs.filter(product__in=selected_products) + kwargs["total"] = qs.annotate( + price=F("quantity") * F("unit_price") + ).aggregate(total=Sum("price", default=0))["total"] kwargs["result"] = qs.select_related( "counter", "counter__club", "customer", "customer__user", "seller" ).order_by("-id") - kwargs["total"] = sum([s.quantity * s.unit_price for s in kwargs["result"]]) - total_quantity = qs.all().aggregate(Sum("quantity")) - if total_quantity["quantity__sum"]: - kwargs["total_quantity"] = total_quantity["quantity__sum"] - benefit = ( - qs.exclude(product=None).all().aggregate(Sum("product__purchase_price")) - ) - if benefit["product__purchase_price__sum"]: - kwargs["benefit"] = benefit["product__purchase_price__sum"] + kwargs["total_quantity"] = qs.aggregate(total=Sum("quantity", default=0))[ + "total" + ] + kwargs["benefit"] = qs.exclude(product=None).aggregate( + res=Sum("product__purchase_price", default=0) + )["res"] kwargs["paginator"] = Paginator(kwargs["result"], self.paginate_by) try: diff --git a/core/templates/core/macros.jinja b/core/templates/core/macros.jinja index 26f2ca17..990dd184 100644 --- a/core/templates/core/macros.jinja +++ b/core/templates/core/macros.jinja @@ -153,7 +153,7 @@ current_page (django.core.paginator.Page): the current page object paginator (django.core.paginator.Paginator): the paginator object #} - {{ paginate_server_side(current_page, paginator, False) }} + {{ paginate_server_side(current_page, paginator, False) }} {% endmacro %} {% macro paginate_htmx(current_page, paginator) %} @@ -168,7 +168,7 @@ current_page (django.core.paginator.Page): the current page object paginator (django.core.paginator.Paginator): the paginator object #} - {{ paginate_server_side(current_page, paginator, True) }} + {{ paginate_server_side(current_page, paginator, True) }} {% endmacro %} {% macro paginate_server_side(current_page, paginator, use_htmx) %} diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 3c790c88..cbf564b3 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-10-17 13:41+0200\n" +"POT-Creation-Date: 2025-11-04 12:43+0100\n" "PO-Revision-Date: 2016-07-18\n" "Last-Translator: Maréchal \n" @@ -893,7 +893,8 @@ msgstr "Administration des mailing listes" msgid "Actions" msgstr "Actions" -#: com/templates/com/mailing_admin.jinja core/templates/core/file_detail.jinja +#: com/templates/com/mailing_admin.jinja com/templates/com/poster_list.jinja +#: core/templates/core/file_detail.jinja #: core/templates/core/file_moderation.jinja sas/templates/sas/moderation.jinja #: sas/templates/sas/picture.jinja msgid "Moderate" @@ -1109,8 +1110,7 @@ msgstr "Vous n'avez pas accès à ce contenu" msgid "Poster" msgstr "Affiche" -#: com/templates/com/poster_edit.jinja com/templates/com/poster_moderate.jinja -#: com/templates/com/screen_edit.jinja +#: com/templates/com/poster_edit.jinja com/templates/com/screen_edit.jinja msgid "List" msgstr "Liste" @@ -1123,25 +1123,13 @@ msgstr "Affiche - modifier" msgid "Create" msgstr "Créer" -#: com/templates/com/poster_list.jinja -msgid "Moderation" -msgstr "Modération" - -#: com/templates/com/poster_list.jinja -msgid "No posters" -msgstr "Aucune affiche" - #: com/templates/com/poster_list.jinja com/templates/com/screen_slideshow.jinja msgid "Click to expand" msgstr "Cliquez pour agrandir" -#: com/templates/com/poster_moderate.jinja -msgid "Posters - moderation" -msgstr "Affiches - modération" - -#: com/templates/com/poster_moderate.jinja -msgid "No objects" -msgstr "Aucun éléments" +#: com/templates/com/poster_list.jinja +msgid "No posters" +msgstr "Aucune affiche" #: com/templates/com/screen_edit.jinja msgid "Screen"