From dcb24b4d195c138bd1cbb56e9124b077ed6223b0 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Wed, 6 Sep 2023 11:33:37 +0200 Subject: [PATCH] admin: prevent too big join by get_search_results (#80932) --- docbow_project/docbow/admin.py | 15 +++++++++++++-- tests/main/test_main.py | 20 ++++++++++++++++---- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/docbow_project/docbow/admin.py b/docbow_project/docbow/admin.py index 7dfdde7..4868b79 100644 --- a/docbow_project/docbow/admin.py +++ b/docbow_project/docbow/admin.py @@ -1,8 +1,12 @@ +import functools +import operator + import django.contrib.admin as admin from django.conf import settings from django.contrib.auth import admin as auth_admin from django.contrib.auth import models as auth_models from django.core.exceptions import PermissionDenied +from django.db.models import Q from django.urls import NoReverseMatch, re_path, reverse from django.utils.safestring import mark_safe from django.utils.translation import gettext as _ @@ -19,6 +23,13 @@ from docbow_project.docbow import actions, auth_views, forms, models, notificati TITLE = "Plate-forme sécurisée d'échange de documents" +class GetSearchResultsMixin: + def get_search_results(self, request, queryset, search_term): + search_fields = self.get_search_fields(request) + orm_lookups = [Q(**{f'{field}__icontains': search_term}) for field in search_fields] + return queryset.filter(functools.reduce(operator.or_, orm_lookups)), False + + class DocbowAdminSite(admin.AdminSite): site_title = TITLE site_header = TITLE @@ -56,7 +67,7 @@ class SendingLimitationAdmin(admin.ModelAdmin): filetypes_list.short_description = _('Limitation des types de fichier') -class MailingListAdmin(admin.ModelAdmin): +class MailingListAdmin(GetSearchResultsMixin, admin.ModelAdmin): list_display = ['name', 'is_active'] list_filter = ['is_active'] search_fields = ['name', 'members__username', 'members__first_name', 'members__last_name'] @@ -235,7 +246,7 @@ class FileTypeAdmin(admin.ModelAdmin): inlines = [FileTypeAttachedFileKindAdmin] -class NotificationAdmin(admin.ModelAdmin): +class NotificationAdmin(GetSearchResultsMixin, admin.ModelAdmin): search_fields = [ 'user__username', 'user__first_name', diff --git a/tests/main/test_main.py b/tests/main/test_main.py index d2fa877..89ffdf9 100644 --- a/tests/main/test_main.py +++ b/tests/main/test_main.py @@ -124,10 +124,10 @@ def test_admin_mailing_list(admin, client): users = [] mls = [] for i in range(20): - users.append(User.objects.create(username='%s' % i)) + users.append(User.objects.create(username='user%s' % i)) sublist = [] for i in range(19, -1, -1): - mls.insert(0, MailingList.objects.create(name='%s' % i)) + mls.insert(0, MailingList.objects.create(name='ml%s' % i)) mls[0].members.set([users[i]]) mls[0].mailing_list_members.set(sublist) sublist = [mls[0]] @@ -137,8 +137,20 @@ def test_admin_mailing_list(admin, client): from django.test.utils import CaptureQueriesContext with CaptureQueriesContext(conn) as context: - resp = client.get('/admin/docbow/mailinglist/?q=' + ('a%20' * 10)) - assert context[-1]['sql'].count('UPPER') == 80 + resp = client.get('/admin/docbow/mailinglist/?q=' + ('user19 ml18 a%20' * 10)) + assert context[-1]['sql'].count('UPPER') == 8 + + from bs4 import BeautifulSoup + + resp = client.get('/admin/docbow/mailinglist/?q=user19') + soup = BeautifulSoup(resp.content) + tags = soup.find_all('th', {'class': 'field-name'}) + assert {tag.text for tag in tags} == {'ml19'} + + resp = client.get('/admin/docbow/mailinglist/?q=ml18') + soup = BeautifulSoup(resp.content) + tags = soup.find_all('th', {'class': 'field-name'}) + assert {tag.text for tag in tags} == {'ml18'} class MailingListCycle(TestCase):