Приветствую вас вновь, дорогие читатели! Публикую очередную статью из цикла немного про Class Based Views (далее CBV) в Django. На этот раз я, как и планировал, хотел бы рассмотреть ListView (отвечает за отображение списка объектов) и DetailView (отвечает за отображение информации об отдельном объекте).
Часть 1, часть 2, часть 3, часть 4
Также, как и ранее, я буду рад обратной связи с читателями — если вы обнаружите ошибку или неточность в статье, то прошу сообщить, сделаем статью лучше вместе.
get_paginate_by
get_allow_empty
get_context_object_name
get_object
get_slug_field
paginate_by
paginator_class
allow_empty
context_object_name
object
slug_field
Несомненно эта операция требуется в абсолютном большинстве проектов и она, к счастью, предусмотрена и разработчиками Django в фреймворке. Методы и атрибуты, описанные в предыдущей части, я думаю не нуждаются в дополнительном описании, поэтому буду учитывать лишь новые встретившиеся.
В случаях, когда необходимо отобразить огромное число объектов, крайне нежелательно выводить их все одновременно. В этом случае требуется механизм для постраничного вывода данных (пагинация). Класс DetailView наследует примесь MultipleObjectMixin, которая реализует требуемый нам функционал. Для определения количества объектов на страницу используется метод get_paginate_by, который по умолчанию возвращает значение атрибута paginate_by. С помощью атрибута мы можем без особых хлопот указать количество выводимых на 1 страницу объектов.
Иногда возникает необходимость реализовать постраничный вывод своим способом, для этого мы можем передать наш класс пагинации атрибуту paginator_class. По умолчанию этот атрибут содержит ссылку на стандартный класс Paginator, который реализован в модуле django.core.paginator. Дважды подумайте, если все же решите его изменить и уточните нет ли требуемой вам функциональности изначально.
Атрибут allow_empty определяет как обработать ситуацию, когда нет ни одного объекта в списке. Если мы установим значение данного атрибута в True (по умолчанию), то будет возвращаться пустой список объектов. В случае значения False будет возвращаться ошибка 404. Значение данного атрибута возвращает метод get_allow_empty. Его же можно использовать, если требуется некоторая дополнительная проверка или изменение логики.
В шаблоне наш список объектов будет доступен по имени, которое задано с помощью атрибута context_object_name (или возвращается методом get_context_object_name, рассматривался в предыдущей статье). Объекты с текущей страницы находятся в переменной с именем object_list. Значение переменной is_paginated (булево значение) определяет разбит ли наш список объектов на страницы. Более подробно про систему пагинации вы можете узнать в документации.
Атрибут object_list хранит список наших объектов. Необходимо помнить о том, что мы должны не забыть передать текущую страницу нашему отображению для корректной работы. Наиболее простой способ — использование именованных групп в нашем файле urls.py
Разбавим описание небольшим примером использования:
А вот, примерно, так это может выглядеть в шаблоне:
Думаю на этом этапе можно закончить рассмотрение функциональности DetailView, если я все же пропустил что-то важное, то прошу сообщить и я дополню статью.
Теперь настала пора рассмотреть функциональность класса DetailView, который отвечает за просмотр отдельного объекта.
Чтобы получить единичный объект, нам необходимо его идентифицировать по какому-нибудь параметру. Обычно для этого используется так называемый уникальный перв��чный ключ (pk, id). Django также позволяет идентифицировать объект по полю slug, которое может быть любым уникальным словом. Разумеется для SEO иногда удобнее использовать именно slug, в случае если объектами выступают статьи или список пользователей (как, например здесь, на Хабре). Однако в других случаях использовать для идентификации slug нет смысла (например просмотр комментария или личного сообщения). В таких случаях используется первичный ключ.
После того, как мы выбрали способ идентификации объекта, мы должны сообщить Django о своем выборе, передав переменную с соответствующим именем с помощью маршрута файла urls.py.
Какой бы способ идентификации объекта мы не приняли, наш объект будет доступен с помощью метода get_object. Этот метод поочередно пытается найти в маршруте переменные pk и slug, в данном случае переменная с именем pk будет обладать большим приоритетом. С помощью метода get_slug_field мы можем переопределить имя поля slug нашей модели. По умолчанию данный метод возвращает значение атрибута slug_field. Наш объект хранится в атрибуте object.
На данном этапе я думаю мы можем закончить рассмотрение классов ListView и DetailView. В следующей статье я постараюсь написать про FormView и работу с формами. Спасибо, что уделили время на прочтение. Как обычно я открыт для критики и сообщений о недочетах. Желаю всем удачных выходных дней!)
Часть 1, часть 2, часть 3, часть 4
Также, как и ранее, я буду рад обратной связи с читателями — если вы обнаружите ошибку или неточность в статье, то прошу сообщить, сделаем статью лучше вместе.
Ссылки для быстрого поиска методов
get_paginate_by
get_allow_empty
get_context_object_name
get_object
get_slug_field
Ссылки для быстрого поиска атрибутов
paginate_by
paginator_class
allow_empty
context_object_name
object
slug_field
Отображение списка объектов
Несомненно эта операция требуется в абсолютном большинстве проектов и она, к счастью, предусмотрена и разработчиками Django в фреймворке. Методы и атрибуты, описанные в предыдущей части, я думаю не нуждаются в дополнительном описании, поэтому буду учитывать лишь новые встретившиеся.
В случаях, когда необходимо отобразить огромное число объектов, крайне нежелательно выводить их все одновременно. В этом случае требуется механизм для постраничного вывода данных (пагинация). Класс DetailView наследует примесь MultipleObjectMixin, которая реализует требуемый нам функционал. Для определения количества объектов на страницу используется метод get_paginate_by, который по умолчанию возвращает значение атрибута paginate_by. С помощью атрибута мы можем без особых хлопот указать количество выводимых на 1 страницу объектов.
Иногда возникает необходимость реализовать постраничный вывод своим способом, для этого мы можем передать наш класс пагинации атрибуту paginator_class. По умолчанию этот атрибут содержит ссылку на стандартный класс Paginator, который реализован в модуле django.core.paginator. Дважды подумайте, если все же решите его изменить и уточните нет ли требуемой вам функциональности изначально.
Атрибут allow_empty определяет как обработать ситуацию, когда нет ни одного объекта в списке. Если мы установим значение данного атрибута в True (по умолчанию), то будет возвращаться пустой список объектов. В случае значения False будет возвращаться ошибка 404. Значение данного атрибута возвращает метод get_allow_empty. Его же можно использовать, если требуется некоторая дополнительная проверка или изменение логики.
В шаблоне наш список объектов будет доступен по имени, которое задано с помощью атрибута context_object_name (или возвращается методом get_context_object_name, рассматривался в предыдущей статье). Объекты с текущей страницы находятся в переменной с именем object_list. Значение переменной is_paginated (булево значение) определяет разбит ли наш список объектов на страницы. Более подробно про систему пагинации вы можете узнать в документации.
Атрибут object_list хранит список наших объектов. Необходимо помнить о том, что мы должны не забыть передать текущую страницу нашему отображению для корректной работы. Наиболее простой способ — использование именованных групп в нашем файле urls.py
url(r'^page/(?P<page>\d+)/$', PrivatePostList.as_view()),Разбавим описание небольшим примером использования:
# coding: utf-8
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.generic.list import ListView
from content.models import Post
class PrivatePostList(ListView):
model = Post
paginate_by = 10
context_object_name = 'posts'
template_name = 'private_posts.html'
@method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
"""
Декорируем диспетчер функцией login_required, чтобы запретить просмотр отображения неавторизованными
пользователями
"""
return super(PrivatePostList, self).dispatch(request, *args, **kwargs)
def get_queryset(self):
"""
Список наших объектов будет состоять лишь из приватных и не удаленных статей
"""
return Post.objects.filter(is_private=True, is_deleted=False)
А вот, примерно, так это может выглядеть в шаблоне:
{% extends 'base.html' %}
{% block title %}Список приватных статей{% endblock %}
{% block content %}
{% for post in object_list %}
<div class="post">
<div class="post_name">{{ post.name }}</div>
<div class="post_text">{{ post.text|safe }}</div>
<div class="post_info">{{ post.author.username }}, {{ post.created_at|date:"d.m.Y в H:i" }}</div>
</div>
{% empty %}
<p>Статей не обнаружено :(</p>
{% endfor %}
{% if paginator.num_pages > 1 %}
{% if page_obj.has_previous %}
<a href="/page{{ page_obj.previous_page_number }}/">←</a>
{% endif %}
<span class="current_page">{{ paginator.number }}</span>
{% if page_obj.has_next %}
<a href="/page{{ page_obj.next_page_number }}">→</a>
{% endif %}
{% endif %}
{% endblock %}
Думаю на этом этапе можно закончить рассмотрение функциональности DetailView, если я все же пропустил что-то важное, то прошу сообщить и я дополню статью.
Просмотр информации об отдельном объекте
Теперь настала пора рассмотреть функциональность класса DetailView, который отвечает за просмотр отдельного объекта.
Чтобы получить единичный объект, нам необходимо его идентифицировать по какому-нибудь параметру. Обычно для этого используется так называемый уникальный перв��чный ключ (pk, id). Django также позволяет идентифицировать объект по полю slug, которое может быть любым уникальным словом. Разумеется для SEO иногда удобнее использовать именно slug, в случае если объектами выступают статьи или список пользователей (как, например здесь, на Хабре). Однако в других случаях использовать для идентификации slug нет смысла (например просмотр комментария или личного сообщения). В таких случаях используется первичный ключ.
После того, как мы выбрали способ идентификации объекта, мы должны сообщить Django о своем выборе, передав переменную с соответствующим именем с помощью маршрута файла urls.py.
url(r'^post/(?P<pk>\d+)/$', PostDetail.as_view()),Какой бы способ идентификации объекта мы не приняли, наш объект будет доступен с помощью метода get_object. Этот метод поочередно пытается найти в маршруте переменные pk и slug, в данном случае переменная с именем pk будет обладать большим приоритетом. С помощью метода get_slug_field мы можем переопределить имя поля slug нашей модели. По умолчанию данный метод возвращает значение атрибута slug_field. Наш объект хранится в атрибуте object.
from django.http import Http404
from django.views.generic.detail import DetailView
from content.models import Post
class PostView(DetailView):
model = Post
context_object_name = 'post'
template_name = 'private_post_detal.html'
def get_object(self):
"""
Для неавторизованного пользователя возвращает 404 ошибку
Конечно мы можем как и в предыдущем примере использовать декоратор login_required
"""
object = super(PostView, self).get_object()
if not self.request.user.is_authenticated():
raise Http404
return object
На данном этапе я думаю мы можем закончить рассмотрение классов ListView и DetailView. В следующей статье я постараюсь написать про FormView и работу с формами. Спасибо, что уделили время на прочтение. Как обычно я открыт для критики и сообщений о недочетах. Желаю всем удачных выходных дней!)
