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 }}
+
+ {% for choice in choices %}
+ -
+
+
+ {% if spec.value %}
+ - Сбросить фильтр
+ {% endif %}
+ {% endfor %}
+
+