Skip to content

Commit ed5620b

Browse files
committed
step 7 done
1 parent 543c729 commit ed5620b

File tree

7 files changed

+166
-3
lines changed

7 files changed

+166
-3
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ dependencies = [
88
"dj-database-url>=2.3.0",
99
"django>=5.2.1",
1010
"django-bootstrap5>=25.1",
11+
"django-filter>=25.1",
1112
"gunicorn>=23.0.0",
1213
"make>=0.1.6.post2",
1314
"psycopg2-binary>=2.9.10",

task_manager/filters.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import django_filters
2+
from django import forms
3+
from django.contrib.auth import get_user_model
4+
from .models import Task, Label
5+
6+
User = get_user_model()
7+
8+
class TaskFilter(django_filters.FilterSet):
9+
status = django_filters.ChoiceFilter(label='Статус', empty_label='Любой')
10+
executor = django_filters.ModelChoiceFilter(
11+
queryset=User.objects.all(),
12+
label='Исполнитель',
13+
empty_label='Любой',
14+
)
15+
labels = django_filters.ModelMultipleChoiceFilter(
16+
queryset=Label.objects.all(),
17+
label='Метки',
18+
widget=forms.CheckboxSelectMultiple,
19+
)
20+
author = django_filters.BooleanFilter(
21+
method='filter_author',
22+
label='Только мои задачи',
23+
widget=forms.CheckboxInput,
24+
)
25+
26+
class Meta:
27+
model = Task
28+
fields = ['status', 'executor', 'labels']
29+
30+
def __init__(self, *args, **kwargs):
31+
super().__init__(*args, **kwargs)
32+
statuses = Task.objects.values_list('status', flat=True).distinct()
33+
choices = [(status, status) for status in statuses if status]
34+
self.filters['status'].extra['choices'] = choices
35+
36+
def filter_author(self, queryset, name, value):
37+
if value:
38+
return queryset.filter(author=self.request.user)
39+
return queryset

task_manager/models.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
from django.db import models
2-
from django.urls import reverse
32
from django.contrib.auth.models import User
43

54
class Status(models.Model):

task_manager/templates/tasks/task_list.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ <h1>Список задач</h1>
88
{% endfor %}
99
{% endif %}
1010

11+
<!-- Форма фильтра -->
12+
<form method="get">
13+
{{ filter.form.as_p }}
14+
<button type="submit">Применить фильтр</button>
15+
</form>
16+
1117
<table>
1218
<tr>
1319
<th>Название</th>
@@ -23,6 +29,8 @@ <h1>Список задач</h1>
2329
<a href="{% url 'task-delete' task.pk %}">Удалить</a>
2430
</td>
2531
</tr>
32+
{% empty %}
33+
<tr><td colspan="3">Задачи не найдены</td></tr>
2634
{% endfor %}
2735
</table>
2836

task_manager/tests/test_filter.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
from django.test import TestCase, RequestFactory
2+
from django.contrib.auth import get_user_model
3+
from task_manager.models import Task, Label
4+
from task_manager.filters import TaskFilter
5+
6+
User = get_user_model()
7+
8+
class TaskFilterTests(TestCase):
9+
def setUp(self):
10+
self.factory = RequestFactory()
11+
self.user1 = User.objects.create_user(username='user1', password='pass')
12+
self.user2 = User.objects.create_user(username='user2', password='pass')
13+
14+
self.label1 = Label.objects.create(name='Bug')
15+
self.label2 = Label.objects.create(name='Feature')
16+
17+
self.task1 = Task.objects.create(
18+
title='Task 1',
19+
description='Desc 1',
20+
status='new',
21+
author=self.user1,
22+
executor=self.user2,
23+
)
24+
self.task1.labels.add(self.label1)
25+
26+
self.task2 = Task.objects.create(
27+
title='Task 2',
28+
description='Desc 2',
29+
status='done',
30+
author=self.user2,
31+
executor=self.user1,
32+
)
33+
self.task2.labels.add(self.label2)
34+
35+
self.task3 = Task.objects.create(
36+
title='Task 3',
37+
description='Desc 3',
38+
status='in_progress',
39+
author=self.user1,
40+
executor=None,
41+
)
42+
# без меток
43+
44+
def test_filter_by_status(self):
45+
data = {'status': 'new'}
46+
filter = TaskFilter(data=data, queryset=Task.objects.all())
47+
self.assertEqual(
48+
list(filter.qs.order_by('id')),
49+
list(Task.objects.filter(status='new').order_by('id'))
50+
)
51+
52+
def test_filter_by_executor(self):
53+
data = {'executor': str(self.user1.id)}
54+
filter = TaskFilter(data=data, queryset=Task.objects.all())
55+
self.assertEqual(
56+
list(filter.qs.order_by('id')),
57+
list(Task.objects.filter(executor=self.user1).order_by('id'))
58+
)
59+
60+
def test_filter_by_labels(self):
61+
data = {'labels': [self.label1.id]}
62+
filter = TaskFilter(data=data, queryset=Task.objects.all())
63+
self.assertEqual(
64+
list(filter.qs.order_by('id')),
65+
list(Task.objects.filter(labels=self.label1).order_by('id'))
66+
)
67+
68+
def test_filter_by_author_true(self):
69+
# Симулируем запрос с автором user1 и фильтром author=True
70+
request = self.factory.get('/tasks/', {'author': 'on'})
71+
request.user = self.user1
72+
filter = TaskFilter(data={'author': True}, queryset=Task.objects.all(), request=request)
73+
self.assertEqual(
74+
list(filter.qs.order_by('id')),
75+
list(Task.objects.filter(author=self.user1).order_by('id'))
76+
)
77+
78+
def test_filter_by_author_false(self):
79+
request = self.factory.get('/tasks/', {'author': ''})
80+
request.user = self.user1
81+
filter = TaskFilter(data={'author': False}, queryset=Task.objects.all(), request=request)
82+
self.assertEqual(
83+
list(filter.qs.order_by('id')),
84+
list(Task.objects.all().order_by('id'))
85+
)
86+

task_manager/views.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
from django.contrib import messages
1414
from .models import Status, Task, Label
1515
from .forms import StatusForm, TaskForm, LabelForm
16-
16+
from .filters import TaskFilter
17+
from django_filters.views import FilterView
1718

1819

1920
def home_view(request):
@@ -212,4 +213,19 @@ def form_valid(self, form):
212213
messages.error(self.request, "Нельзя удалить метку, связанную с задачами")
213214
return redirect(reverse('label-list'))
214215
messages.success(self.request, "Метка успешно удалена")
215-
return super().form_valid(form)
216+
return super().form_valid(form)
217+
218+
219+
class TaskListView(FilterView):
220+
model = Task
221+
filterset_class = TaskFilter
222+
template_name = 'tasks/task_list.html'
223+
context_object_name = 'tasks'
224+
paginate_by = 10 # если нужна пагинация
225+
226+
def get_filterset(self, filterset_class):
227+
# Передаём request в фильтр, чтобы использовать self.request.user
228+
return filterset_class(self.request.GET, queryset=self.get_queryset(), request=self.request)
229+
230+
def get_queryset(self):
231+
return Task.objects.all().order_by('id')

uv.lock

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)