Pull to refresh

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

Reading time5 min
Views38K
Доброго времени суток, уважаемые читатели! Не так давно мной была опубликована первая часть статьи на данную тему. Я хочу еще раз поблагодарить всех пользователей, высказавших конструктивную критику, благодаря которой примеры в статье удалось привести к почти идеальному виду. В то же время я понял, что данный формат подачи материала является неэффективным: мы рассмотрели всего пару методов, реализованных в Class Based View (далее по тексту CBV). Во второй части я решил переработать подачу и далее постараюсь описать максимально возможное количество методов, представленных в API. Постараюсь, также, охватить те методы, которые были упущены в первой части. Очень надеюсь на конструктивную поддержку читателей и надеюсь, что и в дальнейшем у нас получится продуктивный диалог, в результате которого статья станет еще более информативной.


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

Ссылки для быстрого поиска методов

dispatch
get_context_data
get_template_names
get_context_object_name
get
post
put
delete
head
options
trace
render_to_response
get_queryset

Ссылки для быстрого поиска атрибутов

object
template_name
template_name_suffix
context_object_name
http_method_names
queryset
model

Общая информация

Для начала определимся, что такое вообще эти CBV, нужны ли они и где их стоит использовать. Наверное стоит подчеркнуть, что особого выбора нам разработчики Django не предоставляют. Скорее всего уже с 1.4 версии (судя по транку) generic views будут объявлены как deprecated, а в одной из следующих версий могут вообще отказаться от их использования. Поэтому я решил, как это часто бывает, не откладывать изучение в долгий ящик, а начать работу с CBV прямо сейчас. Собственно, за неимением подробной информации, данная статья и будет написана на личных наблюдениях.
Итак, CBV позволяет нам использовать особенности объектно-ориентированного программирования при разработке наших отображений. Теперь мы можем реализовывать базовые классы, несущие определенную функциональность и использовать их как примеси (mixins) для наших отображений.

Все отображения имеют точку входа, который можно считать и конструктором, это метод dispatch. Данный метод принимает аргумент request, являющийся экземпляром объекта HttpRequest, в качестве первого аргумента. В этом его основная схожесть с function based views. Также данный метод принимает все переданные отображению переменные в качестве именованных и неименованных позиционных аргументов (*args/**kwargs). Рассмотрим пару примеров работы с данным методом.
Метод dispatch является наилучшим местом, для использования декораторов. Для их указания необходимо использовать реализованный в django декоратор method_decorator. Допустим нам необходимо предоставить доступ к определенному списку объектов лишь для авторизованных пользователей:

from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.generic import ListView

class PostList(ListView):

    model = Post

    @method_decorator(login_required())
    def dispatch(self, request, *args, **kwargs):
        return super(PostList, self).dispatch(request, *args, **kwargs)


Имейте ввиду, что метод dispatch для корректной работы должен возвращать ссылку на родительский диспетчер. В противном случае не произойдет корректной передачи управления.

Очень часто возникает необходимость передать нашему отображени дополнительный контекст. Допустим у нас есть статья, просмотр которой мы реализуем с помощью класса DetailView. Нам необходимо также получить список комментариев к данной статье. На мой взгляд метод get_context_data
будет лучшей точкой для реализации требуемого функционала.

from django.views.generic.detail import DetailView
from models import Post, Comment

class PostDetail(DetailView):
    
    model = Post

    def get_context_data(self, **kwargs):
        context = super(PostDetail, self).get_context_data(**kwargs)
        context['comments'] = Comment.objects.filter(post=self.object, is_delete=False).order_by('-created_at')
        return context


Несколько забегая вперед уточню, что получить доступ к текущему объекту при реализации DetailView можно через атрибут object.

Если мы хотим узнать (или задать) имя для шаблона, который будет использовать наше отображение, то нам нужно обратить внимание на метод get_template_names. Данный метод возвращает список имен шаблонов, которые Django будет пытаться использовать в порядке их указания. По умолчанию наивысшим приоритетом будет обладать шаблон, указанный в атрибуте template_name нашего объекта. Если данный атрибут не указан, то Django будет пытаться использовать шаблон «название_приложения/имя_объекта_префикс». Например для приложения content и модели post данный путь будет иметь вид «content/post_префикс.html». Префикс задается с помощью атрибута template_name_suffix, либо используется префикс по умолчанию для данного типа отображения. Например для ListView префикс носит имя "_list", для DetailView "_detail". Если мы просто хотим, чтобы наше отображение использовало шаблон, который мы хотим, то нам достаточно лишь указать его с помощью атрибута template_name. Переопределение метода get_template_names может потребоваться, если нам необходимо собирать имя шаблона по своим правилам.

Часто бывает необходимо изменить имя переменной, под которой наш объект (или список объектов) доступен в шаблоне. Метод, отвечающий за данную функциональность носит имя get_context_object_name. Данный метод принимает в качестве аргумента объект (или список объектов). В случае с отдельным объектом имя переменной в шаблоне по умолчанию будет именем самого объекта. В случае со списком объектов это будет имя объекта с суффиксом _list. Например для объекта Post иимена переменных будут post и post_list для отдельного объекта и списка объектов соответственно. Мы можем явно указать имя переменной, если присвоим ее значение атрибуту context_object_name.

Представим, что перед нами стоит задача по реализации RESTful приложения, CBV предоставляет исчерпывающий набор методов для их реализации. Чтобы реализовать работу приложения через определенный метод, нам нужно лишь переопределить одноименный метод в нашем отображении. Список доступных методов: get, post, put, delete, head, options, trace. Работа с формами упрощается в разы, ведь мы можем использовать метод get для отображения нашей формы, а ее обработку перенести в метод post. Требуется добавить какой-либо дополнительный специфический http метод? Нет проблем, достаточно лишь дополнить атрибут http_method_names, содержащий список имен доступных методов, а затем определить одноименный метод у себя в отображении. Все http методы должны возвращать объект HttpResponse. Если нам необходимо отрендерить шаблон нашего отображения, то мы можем для этого вызвать в вышеуказанных http методах метод render_to_response. Данный метод имеет не только такое же имя, как и его функциональный собрат, но и схожую функциональность.

class PostDetail(DetailView):

    model = Post
    
    def get(self, request, **kwargs):
        return self.render_to_response(self.get_context_data(), **kwargs)


Метод принимает в качестве аргумента требуемый контекст.

Прежде чем вывести информацию в шаблон нам необходимо ее получить. В этом нам поможет метод get_queryset, задача которого вернуть объект QuerySet. По умолчанию данный метод возвращает атрибут queryset если он определен, либо список всех объектов модели, которая указана в атрибуте model. Мы можем переопределить данный метод, чтобы он удовлетворял нашим задачам.

class PostList(ListView):
    
    model = Post

    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


В данной части я постарался охватить по возможности все базовые (имеющиеся во всех типах CBV) методы. Если я что-то пропустил, то прошу сообщить и я дополню статью. Следующие части будут включать в себя описание особенностей отдельных типов CBV. Начну, пожалуй, с описания методов ListView и DetailView. Буду очень рад, если мои статьи помогут людям в поиске информации. Спасибо, что прочитали данную статью :)
Tags:
Hubs:
Total votes 10: ↑10 and ↓0+10
Comments9

Articles