Class-based Views или Представления, основанные на классах в Django, часть 1

Class-based Views или Представления, основанные на классах в Django, часть 1

Во многих обучающих материалах, в том числе в официальном туториале, представления организуются в виде методов внутри модуля views. Это достаточно удобный, понятный способ. На первых порах. В дальнейшем, когда такое представление начинает обрастать сложными условиями (разбором параметров запроса, выбором правильного макета, правильным подбором запроса), его становится все сложнее воспринимать. Более того, зачастую для разных представлений необходимо прописывать одни и те же условия. Из-за этого код растет, а объединять код через другие функции бывает не всегда удобно и красиво.

Для упрощения подобной задачи используются представления, основанные на классах. Изначально они создавались для упрощения процесса разработки (когда можно было одной строчкой создать и представление, и макет отображения). Но, начиная с версии 1.5, такие представления стали основным механизмом разработки, достаточно удобным и вовсю используемым как внутри самого Django, так и в большинстве сторонних приложений.

Основные сведения

Основная идея представлений, основанных на классах, в том, что есть несколько базовых классов, способных формировать представления на основе входящих данных. Эти классы самодостаточны и берут на себя львиную долю работы. Однако, за счет ООП, эти классы можно расширять и переопределять. Благодаря этому с одной стороны мы получаем класс, который можно использовать сразу «из коробки», только нацелев его на какую-нибудь модель, с другой стороны, за счет переопределений свойств и методов, мы можем крутить этот класс так, как нам захочется.

Также в таких классах реализована концепция так называемых микс-инов, или смешанных классов. Микс-инами называют дополнительные классы, которые расширяют собой поведение основного класса. Возникают микс-ины в случае наследования, когда мы при создании нового дочернего класса, можем указать не только основной родительский класс, но и несколько микс-инов. Тогда созданный дочерний класс приобретет не только свойства родителя, но и свойства всех микс-инов.

Основные классы предствалений располагаются в пакете django.views.generic. Поэтому их очень часто называют дженериками. Базовым классом является View, от него наследуются два класса общего назначения TemplateView и RedirectView, два класса отображения моделей DetailView и ListView, 4 класса редактирования моделей — FormView, CreateView, UpdateView, DeleteView — и целая куча для представления архивов и моделей с использованием даты — ArchiveIndexView, YearArchiveView, MonthArchiveView, WeekArchiveView, DayArchiveView, TodayArchiveView, DateDetailView.

Перед детальным рассмотрением ещё немного общей информации. Основным методом любого такого представления является метод as_view(). В URL’ах использование этого метода выглядит приблизительно так:

У классов есть свойства, которые меняются в зависимости от конкретного класса. Свойства можно определять при создании дочернего класса или в момент вызова as_view() в качестве именованных параметров. Внутри дженериков все свойства получаются через метод get_<имя_свойства>(), поэтому в случае, если свойство не получается определить одной строкой, то можно сразу переопределять соответствующую функцию.

TemplateView

Наверное, самый простой для понимания класс. Существует, чтобы просто отрендерить шаблон. Самый простой вариант использования — создаем страницу «О нас»:

Вот и появился первый параметр, который встречается и у всех остальных представлений — template_name. В нем хранится имя шаблона, который нужно отрендерить. Для класса TemplateView этот параметр обязательный, другие классы имеют механизм автоматического формирования имени шаблона.

Для примера, можно создать дочерний класс и определить шаблон в нем:

И более сложный вариант дочернего класса. Представление при вызове as_view() принимает в себя параметры запроса в свойство request, и мы этим можем воспользоваться.

Будет такая небольшая дискриминация пользователей Windows. Не особо пригодно для жизни, но показывает, как внутри класса можно получить параметры запроса.

ListView

Этот вариант уже намного интереснее. Такой дженерик существует для отображения списка той или иной модели. Рассмотрим минимальный вариант использования на следующем примере:

В минимальном варианте достаточно указать класс в параметре model, по которому строить список, и создать шаблон. Если не указывать имя шаблона, то оно будет формироваться по следующему алгоритму: <имя_приложения>/<имя_модели>_list.html. В нашем случае это будет blog/post_list.html. Сформированный список по умолчанию попадает в шаблон как параметр object_list.

Теперь можно приступить к усложнению использования данного класса. Случай первый — мы не хотим показывать все записи. Тогда параметр model=Post нас уже не устроит. Если указать только модель, то представление применит к модели .objects.all() и положит результат в параметр queryset. Но если переопределить qeuryset, то model нам больше не понадобится:

Если мы захотим изменить имя, под которым список передается в шаблон, то нам нужен будет параметр context_object_name:

Соответственно через методы get_query_set() и get_context_object_name() можно создать более сложный расчет этих параметров.

Создадим дочерний класс от ListView и я покажу ещё пару интересных вещей:

Предположим, context_object_name мы поменяли. А что делать, если нам необходимо расширить контекст, передаваемый в шаблон? Контекст для шаблона формирует метод get_context_data(), в рамках которого и вызывается get_context_object_name(). При переопределении этого метода следует не забыть вызвать родительский метод, если мы не хотим заново передавать основной объект. Передадим в шаблон параметры запроса:

Осталась ещё такая особенность, как передача параметров, но мы её рассмотрим в следующем представлении.

DetailView

DetailView по многим параметрам похож на ListView, но имеет свои особенности. Как видно из названия, этот дженерик существует для представления конкретной записи модели. Но встает вопрос, как отобрать эту конкретную запись? По умолчанию DetailView позволяет отбирать по полю pk и slug. Например, минимальный вариант использования будет выглядеть так:

Если мы создадим у нашей модели поле slug, то можно будет выполнить поиск по нему.

По аналогии с ListView, DetailView по умолчанию ищет шаблон <имя_приложения>/<имя_модели>_detail.html. Найденная запись передается в шаблон параметром object.

Поля, по каким необходимо искать, задаются в параметрах: slug_field — имя слага в модели, по умолчанию slug, меняем, если вдруг назвали как-то по другому, например seo_slug; slug_url_kwarg — имя параметра в url, который будет показывать слаг, по умолчанию тоже slug; pk_url_kwargs — имя первичного ключа в url, например, можно поменять на id.

Более сложный отбор реализуется через queryset и метод get_object(). Перенесем наш DetailView в модуль views и добавим отбор по queryset:

Теперь поиск будет осуществляться только по опубликованным записям.

Если необходимо обработать какие-то дополнительные параметры из url, то у DetailView (как и у ListView) существуют два свойства: args и kwargs. Следующим примером можно увидеть в командной строке запущенного сервера, что находится в этих свойствах:

Дополнительные параметры в шаблон у DetailView передаются точно так же, как и у ListView (да так же, как и у всех).

На этом первая часть закончена. Во второй части мы познакомимся с дженериками редактирования и очень полезным методом dispatch().

📎📎📎📎📎📎📎📎📎📎