Что такое views в django
Представления (Views) — Python: Разработка на фреймворке Django
Представления (views), или как их ещё называют «джангисты», «вьюхи» — центральные персонажи Web-приложений на основе Django. Ваше приложение может не взаимодействовать с базой данных, может не использовать шаблоны, но views в нём будут обязательно — приложение ведь должно как-то отвечать на запросы! 🙂
В Django используются два вида представлений:
В одном проекте могут одновременно использоваться оба вида. И нельзя сказать, что какой-то один вид лучше другого по всем признакам — у обоих видов есть свои сильные и слабые стороны.
Представления-функции
Так как каждая view function получает полную информацию о запросе, ей одной решать, как этот запрос обрабатывать. Так, например, функция решает, как реагировать на методы HTTP и как обрабатывать параметры запроса:
И раз представление — это обычная функция, к ней можно применять обычные же декораторы! В Django есть несколько полезных. Вот один из них:
Если такая view получит запрос с отличным от указанных методом, то автоматически будет сформирован ответ ResponseNotAllowed (код 405).
View function очень просты концептуально и дают вам полный контроль над обработкой запроса. Это, безусловно, полезно, но заставляет каждый раз писать код вручную. Или подготавливать библиотечку декораторов, упрощающих решение типичных задач.
Представления-классы
Метод get здесь работает точь-в-точь как view function. При этом на каждый запрос будет создан новый экземпляр этого класса, так что вы смело можете объявлять в классе методы, которые по ходу выполнения запроса будут менять его состояние.
Чем же представления-классы хороши? Возможностью лёгкого переиспользования поведения через наследование. Взглянем на встроенный TemplateView из модуля django.views.generic.base в деле:
Нам даже не пришлось наследовать класс, мы просто использовали готовый, указав при регистрации имя шаблона!
Но даже если бы мы унаследовали класс, чтобы передать шаблону какие-то данные (в контексте), то нам бы пришлось переопределить лишь малую часть функциональности класса-предка:
И подобных базовых представлений (base views) Django содержит предостаточно! Особенно хорошо проработаны представления для отображения сущностей из базы данных: здесь вам и отображение списков записей, и отображение отдельных записей в детализированной форме. Сказывается направленность Django на решение задач по разработке контентных сайтов.
Представления-классы хорошо подходят для решения повторяющихся задач. Большая часть из которых, к тому же, уже «почти решена» силами Django.
Class-based views — зачем и как использовать
Мотивация
Идея проста — есть некоторый часто используемый функционал (например, отображение объекта/списка объектов из БД, передача контекста в шаблон, и т.п.), который используется очень часто. Чтобы не приходилось писать однообразный код, подобные view описаны прямо в коде фреймворка. Для того, чтобы использовать их, нужно сообщить только специфические параметры, вроде типа объекта или имени шаблона, в который передавать контекст.
Например, у нас есть класс Publisher, и нам хочется отобразить на странице список всех объектов этого класса. В Django 1.2 мы используем generic view так:
Мы просто передаем в словарь параметров интересующий нас queryset, все остальное уже реализовано за нас.
Generic views на классах
Для того, чтобы в generic views можно было менять все, что угодно, разработчики решили представлять их в виде классов, в которых основные параметры представлены полями, а фунционал наглядно разбит на функции — отдельно диспетчеризация, отдельно формирование контекста, отдельно рендеринг шаблона (подробнее об этом см. ниже). От такого класса можно наследовать свои view, и перекрывать в них переменные и функции вместо передачи их в параметрах. Так выгладит реализация примера с Publisher на классах:
В urls.py же вместо названия «вьюхи» теперь передается метод as_view() описанного класса:
В качестве параметров этой фунции могут быть переданы переменные типа model или template_name, аналогично тому, как это делалось с function-based views.
Теперь, если нам нужно будет, например, поменять контекст, передаваемый в шаблон, можно просто переопределить в этом классе фунцию get_context_data(), которая занимается формированием контекста. И код будет жить там, где ему полагается — во views.py. Осталось только разобраться с тем, какие функции в каких generic view есть, и что они делают.
Примеси
Ответы на вопросы о том, как вы хотите обработать параметры входящего запроса, как будете выбирать данные из БД и как будете формировать ответ часто не зависят друг от друга. Иногда, вам хочется выдать в качестве ответа HTML страницу, иногда — JSON-объект. Если вы хотите написать общий код для обработки запроса и работы с БД, который будет использоваться с обоих случаях, нужно как-то выделить группу всех методов, отвечающих за формирование ответа из контекста. Такую проблему можно решить с помощью паттерна примесей (mixins).
Все «вьюхи» как классы
Использование class-based generic views мотивирует писать все «вьюхи» в виде классов, все из тех же соображений снижения повторяемости и структуризации. Если из function-based generic views вам часто не подходила ни одна — по причинам, описанным выше, то из class-based generic views вам всегда будет полезен View.
Все generic views наследуются от этого класса. В нем реализована функция dispatch(), принимающая request и возвращающая HttpReponse, собственно то, что и делала всегда view, будучи фунцией. Стоит заметить, что функция as_view() как раз возвращает указатель на dispatch(), что делает ясной аналогию с function-based views — в своем минималистичном варианте класс View — класс, содержащий всего одну функцию, которую и вызывает контроллер из URLCONF.
Класс View умеет вызывать свои фунции get(), post() и т.п. в зависимости от типа запроса, передавая request туда.
Пример
Пример простой реализации «вьюхи» в виде класса можно посмотреть, например, здесь. Ниже приведен сложный пример — моя переделка примера из документации — гораздо более полно иллюстрирующий удобство написания «вьюх» в виде классов.
Задача
Проект требует, чтобы любые данные, запрашиваемые клиентом, могли быть возвращены как в виде обычной HTML-страницы, так и в виде JSON-объекта — в зависимости от GET-параметра запроса.
Решение
Нужно написать общий класс «вьюхи», умеющий представлять ответ в двух видах, и от него наследовать все последующие «вьюхи» в проекте.
В django реализован TemplateResponseMixin, функция render_to_response() которого умеет рендерить указанный в template_name шаблон с переданным ей в качестве параметра контекстом.
Напишем свою примесь, которая будет вместо HTML возвращать JSON:
Функцию конвертации контекста в JSON нужно писать уже понимая, что этот контекст из себя представляет, поэтому она не реализована.
Теперь напишем класс, который будет уметь возвращать и HTML, и JSON:
Функция get переопределяет ту, что объявлена во View. В зависимости от GET-параметра, контекст отдается на рендеринг в одну из двух примесей, и возвращает JSON или HTML. В первом случае понадобится функция, определяющая перевод данного контекста в json. Во втором случае понадобится название шаблона, в котором этот контекст рендерить. Любая «вьюха»-наследник этой, должна иметь имплементации двух методов: get_context(request) — формирующего контекст, и convert_context_to_json(context) — переводящего его в JSON, и определенный template_name.
Реализация конечного view могла быть, например, такой:
Заключение
В этой статье я постарался прояснить то, чего мне нехватало в документации Django — идеи, стоящие за class-based views, и способ их использования. Надеюсь, практика такого повсеместного использования generic views и их последующего расширения в тех случаях, когда это необходимо, покажется кому-то полезной.
Спасибо за внимание, буду рад любой критике.
Как работают Django Class-based views
Для новичка, который осваивает Django, представления на основе классов больше похожи на магию чёрного ящика, по крайней мере, у меня при первом знакомстве сложилось именно такое впечатление. Обильные руководства зачастую показывают, какие атрибуты и методы следует определить в вашем классе, чтобы этот ящик работал на вас, но не дают понимания принципа работы.
Я хочу залезть под капот фреймворка и строчка за строчкой разобрать, как же работают представления на основе классов. Надеюсь, что по прочтении, Class-based views уже не будут казаться такими пугающими и я подстегну вас к дальнейшему самостоятельному изучению исходников. Возможно, вы думали о фреймворке как о некой магии, которую невозможно понять, но на самом деле это обычный код, написанный опытными разработчиками.
Background
Я предполагаю, что у читателя есть базовое представление об ООП в Python и опыт создания проекта на Django. Для более комфортного чтения рекомендую также познакомиться со следующими темами:
Несмотря на то, что я старался максимально подкрепить мыслительный процесс исходным кодом, я понимаю, что держать в голове нить повествования может быть сложно. Для облегчения задачи я создал блок-схему, на которой можно увидеть все атрибуты и методы класса, откуда они наследуются, и окинуть процесс взаимодействия методов между собой одним взглядом.
0. URL dispatcher
Работу с представлениями в Django инициализирует диспетчер URL: он сопоставляет запрошенный URL c шаблонами и, находя совпадения, вызывает указанное представление, которому передаётся экземпляр HttpRequest и аргументы, если они имеются в шаблоне.
Давайте посмотрим, как бы выглядел urlpatterns на основе привычного Function-based View (FBV) и на Class-based View (CBV):
Исходный код класса TemplateView
1. Class View
Иерархия наследования класса AboutView
Давайте посмотрим на исходный код View:
Исходный код класса View
Для новичка выглядит устрашающе, но мы пойдем по пути, которым следует Django.
Первая позволяет убедиться в том, что ни один из переданных аргументов не использует HTTP‑глагол в качестве имени (они перечислены в атрибуте http_method_names класса View )
Дело в том, что методы класса, которые названы в честь HTTP‑глаголов, как раз и будут отвечать за их обработку (т.е. метод get() будет отвечать за запрос c методом GET), мы это ещё увидим позднее.
Вторая проверка разрешает нам переопределять только известные атрибуты и методы класса:
и под конец вызываются два wrapper-метода:
, что делает его очень похожим на обычный FBV.
2. View function
Если описать коротко, то именно функция view является главным дирижёром: она создаёт экземпляр класса нашего представления, запускает внутреннюю логику и в итоге возвращает response. Взглянем на код:
который, проходя по словарю **kwargs (но мы-то помним, что на самом деле он **initkwargs :), создаёт атрибуты экземпляра.
После вызова setup() в функции view() выполняется еще одна проверка, чтобы убедиться, что у self есть атрибут запроса:
Сообщение об ошибке даёт нам объяснение, зачем эта проверка нужна: метод setup() может быть переопределён, создавая вероятность того, что атрибут request может быть забыт.
3. dispatch()
Работа метода dispatch() отражает его название:
http_method_not_allowed возвращает HttpResponseNotAllowed со списком методов, которые мы определили для нашего класса.
Он вернёт нам ответ на запрос с заголовком Allow с поддерживаемыми методами.
Но что делать, если нам нужно обработать запрос GET?
4. get()
5. TemplateResponseMixin
Имена атрибутов информативны сами по себе (но если что вы всегда можете заглянуть в документацию).
Точно так же вы можете переопределять остальные параметры нашего класса, чтобы получить необходимое поведение CBV. Конечно, собирать все атрибуты при множественном наследовании выматывающая работа, но здесь нам на помощь приходит сайт Classy Class-Based Views, в котором вы сразу видите все атрибуты и методы класса и его родителей. Теперь не требуется бежать на StackOverflow, чтобы нагуглить ответ, можно использовать перегрузку аттрибутов с пониманием.
В этом миксине берётся дефолтный TemplateResponse (если вы его не переопределяли), ему передаётся контекст и список шаблонов, полученный с помощью несложного метода get_template_names :
который возвращает имя шаблона внутри списка или возбуждает ошибку, если оно не определено.
Заключение
Конечно, разобрав один из наиболее простых CBV сложно сказать, что уверенно ими владеете, впереди вас ждут более интересные Generic display views, такие как DetailView и ListView с более сложными миксинами и разветвлённым наследованием, но хочется верить, что, прочитав этот материал, вы сможете разобраться в них самостоятельно.
А пока давайте подведём итог полученным знаниям:
Вызов функции view() создаёт экземпляр представления и запускает цепочку обработки запроса
Метод dispach() определяет HTTP-глагол и направляет к соответствующему методу, если его нет, возвращает HttpResponse с кодом 405
Полезные ссылки
Classy Class-Based Views — ресурс, позволяющий для каждого CBV увидеть все наследуемые атрибуты и методы
Эффективный Django. Часть 2
Оглавление
Глава 3. Пишем представление ⇧
3.1. Основы представлений
Конечно, как и большинство других фреймворков, Django позволяет вам передавать аргументы в представление через URL. Мы поговорим об этом, когда будем строить наше приложение.
3.2. Общие представления и представления-классы
3.3. Представления-классы (CBV)
Минимальное представление, реализованное как CBV, является наследником класса View doc и реализует поддерживаемые HTTP-методы. Ниже находится очень небольшое представление «Hello, World» написанное нами ранее, но выполненное в виде представления-класса:
3.4. Вывод перечня контактов
Мы начнем с представления, которое выводит список контактов из базы данных.
Базовая реализация представления очень коротка. Мы можем написать его всего-лишь в несколько строк. Для этого в файле views.py нашего приложения contacts наберем следующий код:
3.5. Определяем URL’ы
Давайте добавим в файл addressbook/urls.py URL-шаблон для нашего представления, которое отображает список контактов:
Примечание: Ошибки импортирования конфигурации URL
Django загружает конфигурацию URL очень рано во время старта и пытается импортировать найденные внутри модули. Если хотя бы одна из операций импорта обернется неудачей, сообщение об ошибке может быть немного непонятным. Если ваш проект перестал работать по причине ошибки импорта, попробуйте импортировать конфигурацию URL в интерактивной оболочке. Это позволяет, в большинстве случаев, определить в каком месте возникли проблемы.
3.6. Создание Шаблона
Сейчас, определив URL для нашего представления списка контактов, мы можем испробовать его. Django включает в себя сервер подходящий для целей разработки, который вы можете использовать для тестирования вашего проекта:
Если вы посетите http://localhost:8080/ в вашем браузере, вы все же получите ошибку TemplateDoesNotExists
Примечание переводчика:
localhost указывайте в том случае, если запускаете браузер с того же хоста, где запущен сервер. Если сервер у вас запущен на другом хосте (как у меня) — вместо localhost укажите IP адрес этого хоста (в моем случае это 192.168.1.51 ).
Примечание переводчика:
Django ожидает, что искомый шаблон находится по адресу имя_приложения/templates/имя_приложения/имя_шаблона
Для наших целей, однако, нам не нужен дополнительный слой из структур директорий, так что мы определим шаблон явно, использую свойство template_name нашего представления-класса. Давайте добавим одну строчку в views.py :
Открыв заново (или обновив) в браузере страницу http://localhost:8080/, вы должны будете увидеть как минимум один контакт, созданный нами ранее через интерактивную оболочку.
3.7. Создаем контакты
Добавление информации в базу данных через интерактивную оболочку занимает слишком много времени, так что давайте создадим представление для добавления новых контактов.
Так же как и в случае с выводом списка контактов, воспользуемся одним из общих представлений Django. В файле views.py мы добавим несколько строчек:
Примечание переводчика:
Если вы используете Django версии >= 1.7, то можете добавить к классу CreateContactView дополнительное поле:
Это не обязательно, но с версии Django 1.7 неиспользование этого поля в классах с автоматическим генерированием форм объявлено устаревшим (при выполнении тестов вам об этом сообщат). Если вы его не укажете — то в форме редактирования будут использоваться все поля, но с версии Django 1.8 такое поведение будет удалено.
Шаблон добавления контакта будет немного более сложный, чем шаблон списка контактов, но не слишком. Наш шаблон contacts/templates/edit_contact.html будет выглядеть как то так:
Документация Django 1.9
Следующие три класса реализуют достаточный набор функциональности, чтобы создать представление Django. Вы можете рассматривать их как родительские представления, которые можно использовать как непосредственно, так и в наследовании. Конечно, эти классы не всегда могут удовлетворить все требования к представлению в проекте: в таком случае рекомендуется “присмотреться” к примесям(Mixins) и общим представлениям классам(Generic class-based views)
Самый главный класс в CBV. Все остальные представления-классы наследуются от него.
Диаграмма методов
Пример views.py:
Пример urls.py:
Атрибуты
Список методов HTTP, которые принимает(обрабатывает) данное представление.
Значения по умолчанию:
Методы
classmethod as_view (**initkwargs)¶
Возвращает выполняемое(callable) представление, которое принимает запрос и возвращает ответ:
view часть представления – метод, который принимает аргумент request плюс дополнительные аргументы, и возвращает HTTP ответ(response).
http_method_not_allowed (request, *args, **kwargs)¶
Если представление было вызвано с неподдерживаемым методом HTTP, будет вызван данный метод.
Реализация по умолчанию возвращает HttpResponseNotAllowed со списком разрешенных методов в виде простого текста.
Обрабатывает запросы на определение “глаголов”(методов) OPTIONS HTTP. Возвращает список всех разрешенных для представления методов HTTP.
TemplateView¶
Обрабатывает заданный шаблон, используя контекст(context), содержащий параметры из URL.
Классы-предки, Ancestors (MRO)
Представление наследует методы и атрибуты из следующего представления:
Диаграмма методов
Пример views.py:
Пример urls.py:
Контекст(Context)
Словарь ключевых аргументов, “отловленных” (через ContextMixin ) из шаблона URL, который обрабатывается данным представлением.
RedirectView¶
Редирект на заданный URL.
Классы-предки, Ancestors (MRO)
Представление наследует методы и атрибуты из следующего представления:
Диаграмма методов
Пример views.py:
Пример urls.py:
Атрибуты
Адрес URL для редиректа, в виде строки. Или None для генерации 410 (Gone) HTTP ошибки.
Название URL-шаблона на который перенаправлять. Вычисление URL-а будет выполнено с аргументами, переданными в это представление.
Методы
Создает целевой URL для редиректа
Реализация по умолчанию использует url в качестве отправной строки, выполняет подстановку % параметров в этой строке из именованных параметров, полученных из URL.