Разработка представлений на основе классов
Функции и классы
До сих пор view были функциями. Django также позволяет писать представления на основе классов (class-based views, CBV). Их преимущества:
- код для разных HTTP-методов разделён по методам класса (
get,post), а не поif; - можно наследоваться и переопределять части поведения;
- миксины добавляют функции (например, «только для вошедших»);
- обобщённые представления (generic views) решают типовые задачи — список, детальная страница, создание — почти без кода.
Базовый класс View
from django.http import HttpResponse
from django.views import View
class HelloView(View):
def get(self, request):
return HttpResponse("Привет от GET")
def post(self, request):
return HttpResponse("Привет от POST")
path("hello/", HelloView.as_view(), name="hello")
as_view() превращает класс в функцию, которую понимает диспетчер URL. На каждый запрос создаётся новый экземпляр, метод dispatch() вызывает get() или post() в зависимости от метода запроса, а на неподдерживаемые методы отвечает 405.
TemplateView
Для страниц, которым нужен только шаблон:
from django.views.generic import TemplateView
class AboutView(TemplateView):
template_name = "about.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["posts_count"] = Post.published.count()
return context
Или прямо в urls.py: path("about/", TemplateView.as_view(template_name="about.html")).
ListView для блога
from django.views.generic import ListView
class PostListView(ListView):
queryset = Post.published.all()
context_object_name = "posts"
paginate_by = 3
template_name = "blog/post/list.html"
| Атрибут | Значение |
|---|---|
model = Post |
Модель (альтернатива queryset, тогда берётся Post.objects.all()) |
queryset |
Свой набор объектов |
context_object_name |
Имя списка в шаблоне (по умолчанию object_list и post_list) |
paginate_by |
Объектов на странице |
template_name |
Шаблон (по умолчанию blog/post_list.html) |
ordering |
Сортировка |
Маршрут:
urlpatterns = [
path("", views.PostListView.as_view(), name="post_list"),
...
]
Пагинация в ListView
ListView передаёт в шаблон объект страницы как page_obj. Обновим list.html:
{% include "pagination.html" with page=page_obj %}
Неверный номер страницы ListView обрабатывает сам: ?page=abc или ?page=999 дадут 404.
Динамический набор: get_queryset
Если набор зависит от запроса — переопределите метод:
class PostListView(ListView):
paginate_by = 3
context_object_name = "posts"
template_name = "blog/post/list.html"
def get_queryset(self):
qs = Post.published.select_related("author")
q = self.request.GET.get("q")
if q:
qs = qs.filter(title__icontains=q)
return qs
Внутри CBV доступны self.request, self.args, self.kwargs (параметры из URL).
DetailView
Для одной записи — DetailView:
from django.views.generic import DetailView
class PostDetailView(DetailView):
model = Post
template_name = "blog/post/detail.html"
context_object_name = "post"
По умолчанию ищет объект по pk или slug из URL. Наш адрес с датой сложнее, поэтому для блога оставим функцию post_detail — смешивать функции и классы в одном проекте нормально. Подробно DetailView, CreateView, UpdateView и миксины разберём в проекте «Сайт статей».
Функции или классы?
| Функции | Классы |
|---|---|
| Явный, линейный код | Меньше кода для типовых задач |
| Проще читать новичку | Наследование и миксины |
| Удобно для нестандартной логики | Нужно знать, какие методы переопределять |
Оба подхода официально поддерживаются. Хорошее правило: типовые CRUD-страницы — generic views, нестандартная логика — функции.
Итог
- CBV разделяют обработку HTTP-методов по методам класса, подключаются через
as_view(). TemplateView— страница с шаблоном,ListView— список с пагинацией,DetailView— одна запись.get_queryset()иget_context_data()— главные точки расширения.- ListView передаёт страницу как
page_obj.
Практика решено 0 из 5
Перепишите список постов на ListView: класс PostListView, набор — Post.published.all(),
имя переменной в шаблоне — posts, по 3 на странице, шаблон — blog/post/list.html.
from django.views.generic import ListView
class PostListView(ListView):
queryset = Post.published.all()
context_object_name = "posts"
paginate_by = 3
template_name = "blog/post/list.html"
Подключите PostListView к маршруту "" с именем post_list.
path("", views.PostListView.as_view(), name="post_list")
Как ListView с paginate_by передаёт страницу в шаблон?
Какой шаблон по умолчанию ищет ListView для модели Post из приложения blog?
Какие методы обработчиков HTTP можно определить в классе-наследнике View?
Войдите, чтобы отмечать пройденные уроки.