Django: использование плагина django-filter с CBV
Django-filter — чрезвычайно мощный плагин для Django, который расширяет структуру, предоставляя общий набор API-интерфейсов для простой настройки и управления пользовательскими фильтрами в моделях. Этот плагин предоставляет огромное количество новых функций, при этом избегая дополнительного шаблонного кода для разработчиков.
Этот плагин настолько невероятно полезен, что очень популярный плагин Django Rest Framework построен поверх плагина django-filter для помощи в построении его представлений REST API.
К сожалению, документация django-filters, хотя и очень краткая и хорошо написанная, не дает примера того, как ее интегрировать с представлениями на основе классов. В этой статье я исправлю это, приведя один такой пример.
Наша конечная цель в этой статье — использовать плагин django-filter для очень быстрого написания приложения Django, которое позволяет пользователям просматривать доступные книги в библиотеке и фильтровать их по автору, жанру, рейтингу и другим полям. Конечно, все это будет выполняться только с использованием представлений на основе классов.
Образцы моделей
Ниже приведены модели Django, которые мы будем использовать в нашем примере.
class Author(models.Model): first_name = models.CharField(max_length=64) last_name = models.CharField(max_length=64) class Category(models.Model): name = models.CharField(max_length=64) class Book(models.Model): RATINGS = [ (1, "Trash"), (2, "C-tier"), (3, "B-tier"), (4, "A-tier"), (5, "S-tier"), ] title = models.CharField(max_length=128) author = models.ForeignKey(Author, on_delete=models.CASCADE) category = models.ForeignKey(Category, on_delete=models.CASCADE) librarian_rating = models.IntegerField(choices=RATINGS, help_text="Our local librarian's rating of this book.")
Эти модели предназначены для представления коллекции книг, найденных в библиотеке.
Далее давайте создадим класс django-filter для этой модели, адаптируя примеры, уже представленные в отличной документации по django-filter.
from django_filters.filterset import FilterSet from .models import Book class BookFilter(FilterSet): class Meta: model = Book fields = ['author', 'category', 'librarian_rating']
Как и в случае с любыми классами FilterSet
django-filter, это полностью настраивается по вашему желанию. Обратитесь к документации django-filter, чтобы узнать обо всех доступных параметрах настройки. А пока мы перейдем к подключению этого к нашему CBV.
Подключение фильтра к CBV
Наше представление без фильтров для наших классов Book
и Author
показано ниже. Этот CBV предоставляет нашим конечным пользователям простой способ просматривать все книги одновременно, без какой-либо фильтрации:
from django.views.generic import ListView from .models import Book class BookListView(ListView): template_name = 'books.html' model = Book context_object_name = 'books'
Теперь, если мы хотим предоставить нашим пользователям возможность фильтровать эти книги, нам нужно использовать класс фильтра, который мы определили выше, и подключить его к этому CBV.
Для этого мы воспользуемся миксином FilterView
, который поставляется вместе с плагином django_filters
. Это включает в себя два простых шага:
- Переключение наследования нашего класса с
ListView
наFilterView
в определении класса. - Установка поля
filterset_class
в классе и указание его на классBookFilter
, который мы создали ранее.
from django_filters.views import FilterView from .filters import BookFilter from .models import Book class BookListView(FilterView): template_name = 'books.html' model = Book context_object_name = 'books' filterset_class = BookFilter
Этот обновленный FilterView
поставляется с новой переменной filter
, которая отправляет весь объект FilterSet
в шаблон. Внутри этого объекта находится FilterSet.form
, который является стандартным объектом формы Django, предварительно заполненным всеми полями, необходимыми пользователю для управления настройками фильтра. Мы можем взаимодействовать с ним на стороне шаблона, как и с любой обычной формой:
{% extends 'base.html' %} {% block title %}Books{% endblock %} {% block content %} <h1>Books</h1> <div class="row my-5 mx-3"> <div class="col"> <div class="card"> <form method="get" action=""> <h5 class="card-header">Filters</h5> <div class="card-body"> {{ filter.form.as_p }} </div> <div class="card-footer text-center"> <input class="btn btn-secondary" type="submit" value="Filter" /> </div> </form> </div> </div> </div> <div class="row"> <div class="col"> <table class="table table-striped table-hover"> <thead> <tr> <th scope="col">Title</th> <th scope="col">Author</th> <th scope="col">Category</th> <th scope="col">Rating</th> </tr> </thead> <tbody> {% for book in books %} <tr> <td>{{ book.title }}</td> <td>{{ book.author }}</td> <td>{{ book.category }}</td> <td>{{ book.get_librarian_rating_display }}</td> </tr> {% empty %} <tr><td colspan="6"><i>No books in this filter.</td></tr> {% endfor %} </tbody> </table> </div> </div> {% endblock %}
И вуаля! Это так просто! Благодаря мощи Django и django-filter у нас теперь есть полнофункциональная форма фильтра для пользователя, и мы сделали это, написав очень мало дополнительного кода.