Фильтры Django с django-filter
Django-filter - это мощная библиотека, которая упрощает создание сложных фильтров для списков объектов. Она предоставляет гибкий API для создания фильтров любой сложности.
Установка и настройка
Сначала установи библиотеку:
1pip install django-filter
Добавь в INSTALLED_APPS:
Базовые фильтры
Создай простой фильтр для модели Book:
1import django_filters
2from .models import Book
3
4class BookFilter(django_filters.FilterSet):
5 title = django_filters.CharFilter(lookup_expr='icontains')
6 published_date = django_filters.DateFromToRangeFilter()
7 author = django_filters.ModelChoiceFilter(queryset=Author.objects.all())
8 price = django_filters.RangeFilter()
9 is_available = django_filters.BooleanFilter()
10
11 class Meta:
12 model = Book
13 fields = ['author', 'published', 'genre']
Типы фильтров
CharFilter - для текстовых полей:
NumberFilter - для числовых полей:
DateFromToRangeFilter - для диапазонов дат:
ChoiceFilter - для выбора из списка:
Множественный выбор
Для множественного выбора используй MultipleChoiceFilter:
Кастомные фильтры
Создай собственный фильтр для сложной логики:
1class PriceRangeFilter(django_filters.Filter):
2 def filter(self, qs, value):
3 if not value:
4 return qs
5
6 try:
7 min_price, max_price = map(float, value.split('-'))
8 return qs.filter(price__gte=min_price, price__lte=max_price)
9 except (ValueError, AttributeError):
10 return qs
11
12class BookFilter(django_filters.FilterSet):
13 price_range = PriceRangeFilter(
14 label='Диапазон цен (например: 100-500)'
15 )
16
17 class Meta:
18 model = Book
19 fields = ['title', 'author']
Использование в views
В Class-Based View:
1from django.views.generic import ListView
2from django_filters.views import FilterView
3
4class BookListView(FilterView):
5 model = Book
6 template_name = 'books/book_list.html'
7 filterset_class = BookFilter
8 paginate_by = 20
9
10 def get_context_data(self, **kwargs):
11 context = super().get_context_data(**kwargs)
12 context['total_count'] = Book.objects.count()
13 context['filtered_count'] = context['filter'].qs.count()
14 return context
В Function-Based View:
1def book_list(request):
2 books = Book.objects.all()
3 book_filter = BookFilter(request.GET, queryset=books)
4
5 # Применяем фильтр
6 filtered_books = book_filter.qs
7
8 # Пагинация
9 paginator = Paginator(filtered_books, 20)
10 page_number = request.GET.get('page')
11 page_obj = paginator.get_page(page_number)
12
13 context = {
14 'filter': book_filter,
15 'page_obj': page_obj,
16 'total_count': books.count(),
17 'filtered_count': filtered_books.count(),
18 }
19 return render(request, 'books/book_list.html', context)
Шаблон для фильтров
Создай форму фильтров в шаблоне:
1{% load django_filters %}
2
3<form method="get" class="filter-form">
4 <div class="row">
5 <div class="col-md-3">
6 {{ filter.form.title.label_tag }}
7 {{ filter.form.title }}
8 </div>
9 <div class="col-md-3">
10 {{ filter.form.author.label_tag }}
11 {{ filter.form.author }}
12 </div>
13 <div class="col-md-3">
14 {{ filter.form.genre.label_tag }}
15 {{ filter.form.genre }}
16 </div>
17 <div class="col-md-3">
18 {{ filter.form.price_min.label_tag }}
19 {{ filter.form.price_min }}
20 </div>
21 </div>
22
23 <div class="row mt-3">
24 <div class="col-md-3">
25 {{ filter.form.published_date.label_tag }}
26 {{ filter.form.published_date }}
27 </div>
28 <div class="col-md-3">
29 {{ filter.form.is_available.label_tag }}
30 {{ filter.form.is_available }}
31 </div>
32 <div class="col-md-6">
33 <button type="submit" class="btn btn-primary">Применить фильтры</button>
34 <a href="{% url 'book_list' %}" class="btn btn-secondary">Сбросить</a>
35 </div>
36 </div>
37</form>
38
39<div class="results-info mt-3">
40 <p>Найдено: {{ filtered_count }} из {{ total_count }} книг</p>
41</div>
42
43<div class="books-list">
44 {% for book in page_obj %}
45 <div class="book-item">
46 <h4>{{ book.title }}</h4>
47 <p>Автор: {{ book.author }}</p>
48 <p>Цена: {{ book.price }} ₽</p>
49 </div>
50 {% empty %}
51 <p>По вашему запросу ничего не найдено</p>
52 {% endfor %}
53</div>
54
55{% include 'pagination.html' with page_obj=page_obj %}
Продвинутые техники
Фильтрация по связанным моделям:
1class BookFilter(django_filters.FilterSet):
2 author_name = django_filters.CharFilter(
3 field_name='author__name',
4 lookup_expr='icontains',
5 label='Имя автора'
6 )
7 publisher_city = django_filters.CharFilter(
8 field_name='publisher__city',
9 lookup_expr='icontains',
10 label='Город издательства'
11 )
12
13 class Meta:
14 model = Book
15 fields = ['title', 'genre']
Фильтрация по аннотациям:
1from django.db.models import Count, Avg
2
3class BookFilter(django_filters.FilterSet):
4 min_rating = django_filters.NumberFilter(
5 method='filter_min_rating',
6 label='Минимальный рейтинг'
7 )
8
9 def filter_min_rating(self, queryset, name, value):
10 if value:
11 return queryset.annotate(
12 avg_rating=Avg('reviews__rating')
13 ).filter(avg_rating__gte=value)
14 return queryset
Настройка URL параметров
Создай чистые URL для фильтров:
Кэширование фильтров
Для оптимизации производительности используй кэширование:
1from django.core.cache import cache
2
3class BookFilter(django_filters.FilterSet):
4 def get_queryset(self):
5 cache_key = f"book_filter_{hash(frozenset(self.data.items()))}"
6 queryset = cache.get(cache_key)
7
8 if queryset is None:
9 queryset = super().get_queryset()
10 cache.set(cache_key, queryset, 300) # 5 минут
11
12 return queryset
FAQ
Q: Как создать кастомные фильтры?
A: Создай класс, наследующий от django_filters.Filter, и переопредели filter метод.
Q: Как фильтровать по диапазону дат?
A: Используй DateFromToRangeFilter с виджетом RangeWidget.
Q: Можно ли фильтровать по связанным моделям?
A: Да, используй двойное подчеркивание: author__name__icontains.
Q: Как добавить пагинацию к фильтрам?
A: Используй Django Paginator или встроенную пагинацию в Class-Based Views.
Q: Как оптимизировать производительность фильтров?
A: Используй select_related, prefetch_related и кэширование.
Лучшие практики
- Всегда добавляй labels и help_text для полей фильтров
- Используй подходящие lookup_expr для каждого типа поля
- Группируй связанные фильтры в отдельные секции
- Добавляй кнопку сброса фильтров
- Показывай количество найденных результатов
- Используй пагинацию для больших списков
- Кэшируй результаты фильтрации при необходимости