Немного подробностей про Class Based View, ч.1

29 Янв
2012

Вступление

Здравствуйте, уважаемые читатели! Для многих джанго-разработчиков не секрет, что с версии 1.3+ вместо используемых ранее generic views нам необходимо начинать использовать class based views. А с 1.4+ версии generic views вообще могут стать deprecated. Информация про class based views (далее CBV) в интернете довольно скупа (а в рунете и подавно). Нет, конечно есть превосходные статьи, в том числе тут на блоге. Одна из статей была опубликована буквально на днях. Но я отношусь к той категории людей, что привыкли все изучать на практике и данный топик написан для таких же людей.
В качестве практического задания мной был выбран классический вариант — создание блога. Для того, чтобы использовать возможности CBV по максимуму мы немного усложним задачу. Пусть это будет не просто блог, а блог, с возможностью публиковать приватные статьи, недоступные неавторизованным пользователям.
Дальнейшие рассуждения подразумевают, что читатель ознакомлен с фреймворком Django и имеет навыки в создании проектов.

Собственно с этого мы и начнем — с создания нового проекта. Хм… Пусть проект будет называться habratest.

Работа с проектом

Создание шаблонов и настройку проекта я оставляю на ваших плечах, статья не о том. Я же сразу перейду к нашим моделям и отображениям. Для начала определимся со структурой моделей. Вот на каком варианте остановился я:


# coding: utf-8
# author: damirazo

from django.contrib.auth.models import User
from django.db import models
from django.contrib import admin

class Post(models.Model):
    author = models.ForeignKey(to=User, verbose_name=u'Автор')
    name = models.CharField(max_length=128, verbose_name=u'Название')
    text = models.TextField(verbose_name=u'Текст')
    created_at = models.DateTimeField(auto_now_add=True, verbose_name=u'Дата публикации')
    rating = models.IntegerField(default=0, verbose_name=u'Рейтинг')
    is_private = models.BooleanField(default=False, verbose_name=u'Приватная статья?')
    is_delete = models.BooleanField(default=False, verbose_name=u'Удаленная статья?')

    def __unicode__(self):
        return self.name

    class Meta(object):
        db_table = 'habraposts'
        ordering = ['-created_at',]

class PostAdmin(admin.ModelAdmin):
    list_display = ('name', 'author', 'created_at')
admin.site.register(Post, PostAdmin)




Собственно назначение каждого из полей понятно уже из атрибута verbose_name, поэтому я не буду здесь задерживаться.
Далее необходимо приступить к самому важному из пунктов — создание отображений, которые мы реализуем посредством CBV. Наш файл views.py будет выглядеть следующим образом:


# coding: utf-8
# author: damirazo

from django.views.generic import ListView
from django.views.generic.list import MultipleObjectMixin
from models import Post

class Posts(ListView):
    """
    Список всех доступных статей
    """

    # Нижеуказанные параметры можно также передать данному отображению через метод as_view()
    # url(r'^$', Posts.as_view(context_object_name='posts', template_name='posts.html))
    model = Post
    # Под данным именем наш список статей будет доступен в шаблоне
    context_object_name = 'posts'
    # Название шаблона
    template_name = 'posts.html'
    # Количество объектов на 1 страницу
    paginate_by = 10

    def get_queryset(self):
        qs = Post.objects.filter(is_delete=False).order_by('-created_at')
        if not self.request.user.is_authenticated():
            return qs.exclude(is_private=True)
        return qs

class PostsIndex(Posts):
    """
    Список статей для главной страницы
    """
    template_name = 'index.html'

    def get_queryset(self):
        return super(PostsIndex, self).get_queryset().exclude(rating__lt=10)






Здесь, несмотря на комментарии в коде, я остановлюсь подробнее.
Первое, что мы видим — это функция pagination. Значения ее параметров приведены в качестве комментария. Отмечу лишь, что данная функция приведена в документации django, поэтому мы переходим сразу к рассмотрению следующих.
Первый встреченный нами класс носит имя PaginatedList. Данный класс наследуется непосредственно от класса ListView, который используется для просмотра списка объектов. В нашем классе PaginatedView мы переопределяем метод get_queryset, который возвращает объект класса Queryset. С помощью переопределенного метода мы ищем в параметрах, переданных данному отображению переменную page. Забегая вперед уточню, что данная переменная должна быть явно определена в маршрутах файла urls.py. Значение переменной (либо единица, при ее отсутствии) передается функции pagination для образования списка страниц. Отсюда вывод, что наш класс PaginatedList возвращает объект Queryset, разбитый на страницы.
Затем, используя данный класс в качестве родительского, мы создаем класс Posts. Используя родительский класс PaginatedList нам не надо думать о разбивке страниц класса Posts — он сам по умолчанию вернет список объектов, разбитый на страницы. В классе Posts мы переопределяем метод dispatch. Данный метод играет роль конструктора класса (вызывается при вызове данного отображения, принимает объект request). Имейте ввиду, что для нормальной работы метод dispatch должен возвращать ссылку на метод родителя, что и реализовано в примере. В данном методе мы используем разделение на авторизованных и неавторизованных пользователей — каждый из них получит свой список объектов. В данном примере мы, для неавторизованных пользователей, исключаем из списка статей все, имеющие значение атрибута is_private равным True.
Следующий метод, который используется в данном примере носит имя PostsIndex. Он будет использоваться для отображения статей на главной странице. По аналогии с блогом там будут отображаться статьи, которые имеют рейтинг равный или больший, чем 10. В данном классе мы вновь переопределяем метод dispatch. На этот раз из объекта Queryset (списка наших статей) исключаем также статьи, рейтинг которых менее 10. Так, как в родительском классе Posts прошла фильтрация объекта Queryset для пользователей и неавторизованных пользователей, то в классе PostIndex мы сразу получаем объект, доступный данному пользователю (или гостю). Поэтому одно из основных преимуществ CBV в возможности использовать все средства ООП при работе с отображениями.
Нам осталось лишь рассмотреть, как будут выглядеть маршруты в нашем проекте:


# Укажем дополнительный маршрут, чтобы главная страница была доступна без указания номера страницы
    # Отдельный список статей для главной страницы (рейтинг которых выше, либо равен, 10)
    url(r'^$', PostsIndex.as_view()),
    url(r'^page(?P\d+)/$', PostsIndex.as_view()),

    # Список всех активных статей
    url(r'^posts/$', Posts.as_view()),
    url(r'^posts/page(?P\d+)/$', Posts.as_view())


Собственно на данном моменте можно закончить первый этап. Если данный формат статей заинтересует блогапользователей, то я опубликую еще серию статей. Следующей следует ожидать просмотр деталей отдельного объекта (например просмотр статьи). Затем планирую публикации статей по работе с формами (публикация статьи, редактирование статьи).

P.S. Если вы обнаружите ошибки или неточности в статье, то добро пожаловать в личные сообщения или комментарии. Также буду очень рад советам профессионалов, которые смогут расставить все точки над «i» и рассказать о моих ошибках.

Желаю всем блогачитателям счастливых выходных и удачи :).

UPD. Выложил тестовый проект, включающий данный пример. Не забудьте отредактировать файл settings.py под свои нужды. Проект можно найти здесь. Если вы обнаружите какие-либо ошибки или недоработки, то прошу сообщить. Часть 2.
По материалам Хабрахабр.



загрузка...

Комментарии:

Наверх