Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion projects/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = (
Expand All @@ -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 = (
(
Expand Down
40 changes: 40 additions & 0 deletions projects/tests/test_project_admin.py
Original file line number Diff line number Diff line change
@@ -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, '<input type="hidden" name="draft__exact" value="1">', html=True)
self.assertContains(response, 'href="?draft__exact=1&amp;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), [])
23 changes: 23 additions & 0 deletions templates/admin/projects/leader_filter.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<details data-filter-title="{{ title }}" open>
<summary>{{ title }}</summary>
<ul>
{% for choice in choices %}
<li>
<form method="get">
{% for name, value in choice.params %}
<input type="hidden" name="{{ name }}" value="{{ value }}">
{% endfor %}
<label for="project-leader-filter">Email или ID пользователя</label>
<p>
<input type="search" id="project-leader-filter" name="{{ spec.parameter_name }}"
value="{{ spec.value|default_if_none:'' }}" size="18" maxlength="254">
</p>
<button type="submit">Найти</button>
</form>
</li>
{% if spec.value %}
<li><a href="{{ choice.reset_url }}">Сбросить фильтр</a></li>
{% endif %}
{% endfor %}
</ul>
</details>
Loading