Вступление
Здравствуйте, уважаемые читатели! Для многих джанго-разработчиков не секрет, что с версии 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 под свои нужды. Проект можно найти здесь. Если вы обнаружите какие-либо ошибки или недоработки, то прошу сообщить.
