Представления-классы (Class-based views, CBV)

Представление, в самом общем виде, - это исполняемый(callable) объект, который принимает “на вход” запрос (request), и возвращает ответ(response). В этой роли может выступать не только функция, но и классы - и Django предоставляет нам такие классы и примеры их использования. Такие классы позволяют создавать структурированные, повторно используемые представления, базируясь на возможностях наследования и, в том числе, множественного наследования, - с использованием примесей(mixins). Django включает в себя набор общих(generic) представлений, которые идеально подходят для решения ряда рутинных задач(мы рассмотрим их позже). Ну и конечно, вы можете использовать существующие классы Django для создания собственных, расширяемых и повторно используемых представлений, которые будут полностью удовлетворять вашим потребностям. За более полной информацией обратитесь к документация по классам-представлениям.

Простые примеры

Django предлагает нам базовый набор классов представлений, который может использоваться в широком спектре приложений. Все классы-представления наследуют класс View, который обрабатывает “привязку” представления с соответствующими URL, диспетчеризацию HTTP запросов(анализ методов GET, POST и последующий вызов одноименного метода для обработки запроса) и ряд других простых действий. Класс RedirectView служит для простого HTTP перенаправления(redirect), а класс TemplateView расширяет базовый класс, предоставляя возможность обработки шаблонов.

Использование CBV в ваших URLconf

Самый простой способ использования общих классов-представлений, - это их создание непосредственно в вашем URLconf. Если вы переопределяете всего несколько простых атрибутов класса, вы можете просто передать эти значения как аргументы метода as_view():

from django.conf.urls import url
from django.views.generic import TemplateView

urlpatterns = [
    url(r'^about/', TemplateView.as_view(template_name="about.html")),
]

Любые аргументы, переданные в as_view() переопределят значения этих же аргументов, указанных в теле класса. В данном примере, мы назначаем атрибут template_name для класса TemplateView. Такой же принцип может быть использован для переопределения атрибута url в классе RedirectView.

Использование общих классов-представлений в наследовании

Второй, более мощный и гибкий способ использования общих CBV, это использование их в качестве родительских классов. Вы можете указать эти классы в качестве родительских для вашего пользовательского класса-представления и переопределить в теле вашего класса значения атрибутов (таких как template_name) или методы (такие как get_context_data), определив тем самым специфику его работы. Рассмотрим к примеру представление, которое просто отображает один шаблон , about.html. Django предоставляет нам общий класс-представление TemplateView для подобных задач. Все что нам надо сделать, - унаследовать этот класс в дочернем, пользовательском, классе и переопределить имя шаблона:

# some_app/views.py
from django.views.generic import TemplateView

class AboutView(TemplateView):
    template_name = "about.html"

Теперь необходимо добавить представление в URLconf. TemplateView является классом, а не функцией, поэтому URL должен указывать на метод as_view(), который работает как обычное представление-функция:

# urls.py
from django.conf.urls import url
from some_app.views import AboutView

urlpatterns = [
    url(r'^about/', AboutView.as_view()),
]

Для дополнительной информации по использованию общих(generic) представлений-классов, смотрите раздел общие классы-представления.

Поддержка других методов HTTP

Предположим, кто-то хочет получить доступ к списку книг в нашей библиотеке, используя для этого HTTP и наши представления в качестве API. Клиент, работающий с API, будет подключаться к сайту и получать данные о книгах с момента последнего визита. Но если новых книг не появилось, то мы сталкиваемся с проблемой непродуктивного расходования времени CPU, создания ненужных запросов к БД: все это лишь с целью того, чтобы вернуть пользователю ответ, “что новых книг нет”. Было бы предпочтительней реализовать в API метод, проверяющий наличие обновлений( дату появления последней новой книги).

В URLconf мы создаем связь URL с представлением, выводящим список книг:

from django.conf.urls import url
from books.views import BookListView

urlpatterns = [
    url(r'^books/$', BookListView.as_view()),
]

И, собственно, само представление:

from django.http import HttpResponse
from django.views.generic import ListView
from books.models import Book

class BookListView(ListView):
    model = Book

    def head(self, *args, **kwargs):
        last_book = self.get_queryset().latest('publication_date')
        response = HttpResponse('')
        # RFC 1123 date format
        response['Last-Modified'] = last_book.publication_date.strftime('%a, %d %b %Y %H:%M:%S GMT')
        return response

Если представление выполняется “в контексте” GET запроса, то возвращается просто список объектов(используя шаблон book_list.html). Но если клиент выполнит запрос с методом HEAD, то ответ(response) будет иметь пустое тело и установленный в нужное значение заголовок Last-Modified, хранящий дату последних изменений в нашей “библиотеке”. Основываясь на этой информации, клиент должен принять решение, нужно или нет получить и скачать полный список объектов.