Pull to refresh

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

Django *
Вступление

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


Часть 1, часть 2, часть 3, часть 4

Собственно с этого мы и начнем — с создания нового проекта. Хм… Пусть проект будет называться 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 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)


Здесь, несмотря на комментарии в коде, я остановлюсь подробнее.
Класс Posts наследуется непосредственно от ListView, в котором реализована поддержка списков, разбитых на страницы. Мы переопределяем метод get_queryset, в котором осуществляем проверку на то авторизован ли пользователь на сайте. Для неавторизованных пользователей (гостей) мы исключаем из исходного queryset все объекты, значение атрибута is_private которых равняется True.
Следующий класс, который используется в данном примере носит имя PostsIndex. Он будет использоваться для отображения статей на главной странице. По аналогии с хабром там будут отображаться статьи, которые имеют рейтинг равный или больший, чем 10. В данном классе мы вновь переопределяем метод get_queryset. На этот раз из объекта Queryset (списка наших статей) исключаем также статьи, рейтинг которых менее 10. Так, как в родительском классе Posts прошла фильтрация объекта Queryset для пользователей и неавторизованных пользователей, то в классе PostIndex мы сразу получаем объект, доступный данному пользователю (или гостю). Поэтому одно из основных преимуществ CBV в возможности использовать все средства ООП при работе с отображениями.
Нам осталось лишь рассмотреть, как будут выглядеть маршруты в нашем проекте:

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

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


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

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

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

UPD. Обновил пример в статье (спасибо пользователю marazmiki). А также выложил тестовый проект, включающий данный пример. Не забудьте отредактировать файл settings.py под свои нужды. Проект можно найти здесь. Если вы обнаружите какие-либо ошибки или недоработки, то прошу сообщить.
Tags:
Hubs:
Total votes 22: ↑20 and ↓2 +18
Views 29K
Comments Comments 19