Django: Представления
Django предоставляет базовые классы представлений, которые подойдут для широкого спектра задач. Все представления наследуются от класса View, который отрабатывает связывание представление с URL адресами , диспетчиризацией методов HTTP и другими простыми функциями , такими как RedirectView для простого перенаправления http и TemplateView для расширения базового класа по отрисовке шаблона.
Пример использования в менеджере URL :
Самый простой способ использовать универсальные представления-создать их непосредственно в URLconf. Если вы изменяете только несколько простых атрибутов в представлении на основе классов, вы можете просто передать их в сам вызов метода as_view:
from django.urls import path
from django.views.generic import TemplateView
urlpatterns = [
path('about/', TemplateView.as_view(template_name="about.html")),
]
Любые аргументы, передаваемые в as_view (), будут переопределять атрибуты, установленные в классе. В данном примере мы устанавливаем template_name на TemplateView. Аналогичный шаблон переопределения можно использовать для атрибута url в RedirectView.
Создание подклассов представлений
Второй, более эффективный способ использования универсальных представлений-наследование от существующего представления и переопределение атрибутов (таких как 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.urls
import path from some_app.views
import AboutView
urlpatterns = [
path('about/', AboutView.as_view()),
]
Поддержка других методов HTTP
Предположим, кто - то хочет получить доступ к нашей библиотеке книг по HTTP, используя представления в качестве API. Клиент API будет подключаться время от времени и загружать данные книги для книг, опубликованных с момента последнего посещения. Но если с тех пор никаких новых книг не появилось, это пустая трата процессорного времени и пропускной способности, чтобы получить книги из базы данных, сделать полный ответ и отправить его клиенту. Возможно, было бы предпочтительнее спросить API, когда была опубликована последняя книга.
Мы должны сопоставить URL книги в менеджере URL:
from django.urls import path
from books.views import BookListView
urlpatterns = [
path('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, ответ содержит пустое тело, а последний измененный Заголовок указывает, когда была опубликована последняя книга. На основании этой информации клиент может загрузить или не загрузить полный список объектов.
Представления на основе классов
Классы не заменяют представления на основе функций, но имеют определенные различия и преимущества по сравнению с представлениями на основе функций: Организация кода, связанного с конкретными методами HTTP (GET, POST и др.) можно обращаться к отдельным методам вместо условного ветвления. Объектно-ориентированные методы, такие как mixins (множественное наследование), можно использовать для факторизации кода в повторно используемые компоненты.
В начале были только задействованы функции view, Django передавал функцию HttpRequest и ожидал обратно HttpResponse. Для абстрагирования шаблонов и упрощения разработки представлений для общих случаев были введены универсальные представления на основе функций.Однако способ реализации решения с помощью mixins предоставляет инструментарий, который позволяет сделать универсальные представления на основе классов более расширяемыми и гибкими, чем их аналоги на основе функций. . Инструментарий базовых классов и микшеров, который Django использует для построения универсальных представлений на основе классов, построен для максимальной гибкости.
Например, вместо того, чтобы ограничиваться атрибутом на основе класса для form_class, реализация использует метод get_form, который вызывает метод get_form_class, который в своей реализации по умолчанию просто возвращает атрибут form_class класса. Это дает вам несколько вариантов для определения, какую форму использовать, от простого атрибута, до полностью динамического, вызываемого крюка. Кажется, что добавляет сложность для простых ситуаций, но без них, более продвинутые дизайны были бы ограничены.
Использование представлений на основе классов
По своей сути, представление на основе классов позволяет реагировать на различные методы HTTP-запросов с различными методами экземпляра класса, а не с условно ветвящимся кодом внутри одной функции представления. Так где код для обработки http-запроса Get в вид функции будет выглядеть так:
TemplateView Наверное, самый простой для понимания класс. Существует, чтобы просто отрендерить шаблон. Самый простой вариант использования - создаем страницу "О нас":
from django views.generic import TemplateView
... url(r'^about/', TemplateView.as_view(template_name='about.html'), name='about'),
Вот и появился первый параметр, который встречается и у всех остальных представлений - template_name. В нем хранится имя шаблона, который нужно отрендерить. Для класса TemplateView этот параметр обязательный, другие классы имеют механизм автоматического формирования имени шаблона. Для примера, можно создать дочерний класс и определить шаблон в нем:
# views.py
from django.views.generic import TemplateView
class AboutView(TemplateView):
template_name = 'about.html'
# urls.py
from app.views import AboutView
... url(r'^about/', AboutView.as_view(), name='about'),
И более сложный вариант дочернего класса. Представление при вызове as_view() принимает в себя параметры запроса в свойство request, и мы этим можем воспользоваться.
# views.py
from django.views.generic import TemplateView
class AboutView(TemplateView):
get_template_names(self):
# именно names, не name
if self.request.META['OS'] == 'Windows_NT':
return 'about_win.html'
else:
return 'about.html'
Будет такая небольшая дискриминация пользователей Windows. Не особо пригодно для жизни, но показывает, как внутри класса можно получить параметры запроса.
ListView
Этот вариант уже намного интереснее. Такой дженерик существует для отображения списка той или иной модели. Рассмотрим минимальный вариант использования на следующем примере:
# blog/models.py
from django.db import models
class Post(models.Model):
title = models.CharField('Title',
max_length=50)
slug = models.SlugField('Slug')
content = models.TextField('Content')
draft = models.BooleanField('Draft', default=True)
# blog/urls.py
from django.conf.urls import patterns, url
from django.views.generic import ListView
from blog.models import Post
urlpatterns = patterns('', url(r'^$', ListView.as_view(model=Post), name='blog-post-list'), )
В минимальном варианте достаточно указать класс в параметре model, по которому строить список, и создать шаблон. Если не указывать имя шаблона, то оно будет формироваться по следующему алгоритму: /_list.html. В нашем случае это будет blog/post_list.html. Сформированный список по умолчанию попадает в шаблон как параметр object_list. Теперь можно приступить к усложнению использования данного класса. Случай первый - мы не хотим показывать все записи. Тогда параметр model=Post нас уже не устроит. Если указать только модель, то представление применит к модели .objects.all() и положит результат в параметр queryset. Но если переопределить qeuryset, то model нам больше не понадобится:
# blog/urls.py
... urlpatterns = patterns('',
url(r'^$', ListView.as_view(queryset=Post.objects.filter(draft=False)), name='blog-post-list'),
)
Если мы захотим изменить имя, под которым список передается в шаблон, то нам нужен будет параметр context_object_name:
# blog/urls.py
... urlpatterns = patterns('',
url(r'^$', ListView.as_view(queryset=Post.objects.filter(draft=False), context_object_name='posts'), name='blog-post-list'),
)
Соответственно через методы get_query_set() и get_context_object_name() можно создать более сложный расчет этих параметров. Создадим дочерний класс от ListView и я покажу ещё пару интересных вещей:
# blog/views.py
from django.views.generic import ListView
from blog.models import Post
class PostListView(ListView):
queryset = Post.objects.filter(draft=False) context_object_name = 'posts'
# blog/urls.py
from django.conf.urls import patterns, url
from blog.views import PostListView
urlpatterns = patterns('', url(r'^$', PostListView.as_view(), name='blog-post-list'),
)
Предположим, context_object_name мы поменяли. А что делать, если нам необходимо расширить контекст, передаваемый в шаблон? Контекст для шаблона формирует метод get_context_data(), в рамках которого и вызывается get_context_object_name(). При переопределении этого метода следует не забыть вызвать родительский метод, если мы не хотим заново передавать основной объект. Передадим в шаблон параметры запроса:
# blog/views.py
...
class PostListView(ListView):
...
def get_context_data(self, **kwargs):
context = super(PostListView, self).get_context_data(**kwargs)
context['request'] = self.request
return context
Осталась ещё такая особенность, как передача параметров, но мы её рассмотрим в следующем представлении.
DetailView
DetailView по многим параметрам похож на ListView, но имеет свои особенности. Как видно из названия, этот дженерик существует для представления конкретной записи модели. Но встает вопрос, как отобрать эту конкретную запись? По умолчанию DetailView позволяет отбирать по полю pk и slug. Например, минимальный вариант использования будет выглядеть так:
# blog/urls.py
from django.conf.urls import patterns, url
from django.views.generic import DetailView
from blog.models import Post
urlpatterns = patterns('',
url(r'^detail/(?P\d+)$', DetailView.as_view(model=Post), name='blog-post-detail'),
)
Если мы создадим у нашей модели поле 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:
# blog/views.py
from django.views.generic import DetailView
from blog.models import Post
class PostDetailView(DetailView):
queryset = Post.objects.filter(draft=False)
# blog/urls.py
from django.conf.urls import patterns, url
from blog.views import PostDetailView
urlpatterns = patterns('',
url(r'^detail/(?P\d+)$', PostDetailView.as_view(), name='blog-post-detail'),
)
Теперь поиск будет осуществляться только по опубликованным записям. Если необходимо обработать какие-то дополнительные параметры из url, то у DetailView (как и у ListView) существуют два свойства: args и kwargs. Следующим примером можно увидеть в командной строке запущенного сервера, что находится в этих свойствах:
class PostDetailView(DetailView):
...
def get_object(self, **kwargs):
print(self.args) print(self.kwargs)
return super(PostDetailView, self).get_object(**kwargs)
Дополнительные параметры в шаблон у DetailView передаются точно так же, как и у ListView.