From 49bf3c55d120ec2cf591dcc69562144ec71dfe79 Mon Sep 17 00:00:00 2001 From: Peter <81633644+TThanos3000@users.noreply.github.com> Date: Sun, 8 Nov 2026 22:16:10 +0300 Subject: [PATCH] Filter projects by leader in Django admin --- projects/admin.py | 36 ++++++++++++++++++- projects/tests/test_project_admin.py | 40 +++++++++++++++++++++ templates/admin/projects/leader_filter.html | 23 ++++++++++++ 3 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 projects/tests/test_project_admin.py create mode 100644 templates/admin/projects/leader_filter.html diff --git a/projects/admin.py b/projects/admin.py index d9739705..01bc5dd6 100644 --- a/projects/admin.py +++ b/projects/admin.py @@ -47,6 +47,40 @@ def get_formset(self, request, obj=None, **kwargs): return formset +class ProjectLeaderFilter(admin.SimpleListFilter): + title = "Лидер" + parameter_name = "leader" + template = "admin/projects/leader_filter.html" + + def lookups(self, request, model_admin): + return () + + def has_output(self): + return True + + def choices(self, changelist): + yield { + "params": [ + (name, value) + for name, value in changelist.params.items() + if name != self.parameter_name + ], + "reset_url": changelist.get_query_string({self.parameter_name: None}), + } + + def queryset(self, request, queryset): + value = (self.value() or "").strip() + if not value: + return queryset + if value.isdecimal() and len(value) <= 19: + leader_id = int(value) + if leader_id <= 2**63 - 1: + return queryset.filter(leader_id=leader_id) + if "@" in value and len(value) <= 254 and "\x00" not in value: + return queryset.filter(leader__email__iexact=value) + return queryset.none() + + @admin.register(Project) class ProjectAdmin(admin.ModelAdmin): list_display = ( @@ -61,7 +95,7 @@ class ProjectAdmin(admin.ModelAdmin): ) list_display_links = ("id", "name") search_fields = ("name",) - list_filter = ("draft", "is_public", "is_company", "trl") + list_filter = (ProjectLeaderFilter, "draft", "is_public", "is_company", "trl") fieldsets = ( ( diff --git a/projects/tests/test_project_admin.py b/projects/tests/test_project_admin.py new file mode 100644 index 00000000..b1abd4bf --- /dev/null +++ b/projects/tests/test_project_admin.py @@ -0,0 +1,40 @@ +from django.test import TestCase +from django.urls import reverse + +from .helpers import create_collaborator, create_project, create_staff_user, create_user + + +class ProjectLeaderFilterTests(TestCase): + @classmethod + def setUpTestData(cls): + cls.admin = create_staff_user() + cls.leader = create_user() + cls.draft = create_project(leader=cls.leader, name="Leader draft") + cls.published = create_project(leader=cls.leader, name="Leader published", draft=False) + cls.other = create_project(name="Other project") + create_collaborator(cls.other, user=cls.leader) + + def setUp(self): + self.client.force_login(self.admin) + self.url = reverse("admin:projects_project_changelist") + + def test_filters_by_leader_id_or_email(self): + for leader in (str(self.leader.pk), self.leader.email.upper()): + with self.subTest(leader=leader): + response = self.client.get(self.url, {"leader": leader}) + self.assertContains(response, 'id="project-leader-filter"') + self.assertContains(response, "Сбросить фильтр") + self.assertCountEqual(response.context["cl"].result_list, [self.draft, self.published]) + + def test_combines_with_search_and_draft_filter(self): + response = self.client.get(self.url, {"leader": self.leader.pk, "draft__exact": "1", "q": "Leader"}) + + self.assertContains(response, '', html=True) + self.assertContains(response, 'href="?draft__exact=1&q=Leader"') + self.assertEqual(list(response.context["cl"].result_list), [self.draft]) + + def test_invalid_leader_id_returns_no_projects(self): + response = self.client.get(self.url, {"leader": "9223372036854775808"}) + + self.assertEqual(response.status_code, 200) + self.assertEqual(list(response.context["cl"].result_list), []) diff --git a/templates/admin/projects/leader_filter.html b/templates/admin/projects/leader_filter.html new file mode 100644 index 00000000..42198455 --- /dev/null +++ b/templates/admin/projects/leader_filter.html @@ -0,0 +1,23 @@ +
+ {{ title }} + +