Spec-Zone.ru › Django 2.1

Введение в представления на основе классов

Представления на основе классов предоставляют альтернативный способ реализации представлений как объектов Python вместо функций. Они не заменяют представления на основе функций, но имеют определенные отличия и преимущества по сравнению с ними:

  • Организация кода, связанного со специфическими методами HTTP (GET, POST, и т.д.), может быть реализована отдельными методами вместо условного ветвления.
  • Можно использовать объектно-ориентированные техники, такие как миксины (множественное наследование), для факторизации кода в переиспользуемые компоненты.

Взаимосвязь и история общих представлений, представлений на основе классов и общих представлений на основе классов

Вначале существовал только контракт функции представления; Django передавал вашей функции HttpRequest и ожидал в ответ HttpResponse. Это было всё, что предоставляло Django.

Вскоре было признано, что в разработке представлений существуют общие идиомы и шаблоны. Были представлены общие представления на основе функций для абстрагирования этих шаблонов и облегчения разработки представлений в общих случаях.

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

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

Если вы в прошлом пробовали общие представления на основе функций и обнаружили их недостаточными, вы не должны рассматривать общие представления на основе классов как просто классовый эквивалент, а скорее как новый подход к решению исходных проблем, которые общие представления должны были решить.

Набор базовых классов и миксинов, используемых Django для построения общих представлений на основе классов, разработан для максимальной гибкости и, как следствие, имеет множество крючков в виде реализаций методов по умолчанию и атрибутов, которые, скорее всего, не будут беспокоить вас в самых простых случаях использования. Например, вместо того, чтобы ограничивать вас атрибутом на основе класса для form_class, реализация использует метод get_form, который вызывает метод get_form_class, который в своей реализации по умолчанию просто возвращает атрибут form_class класса. Это предоставляет вам несколько вариантов для указания используемого формата, от простого атрибута до полностью динамического вызываемого крючка. Эти варианты, возможно, добавляют ненужную сложность для простых ситуаций, но без них более сложные конструкции были бы ограничены.

Использование представлений на основе классов

В основе своей представление на основе класса позволяет реагировать на различные методы запросов HTTP с помощью различных методов экземпляра класса вместо условного ветвления кода внутри единственной функции представления.

Таким образом, код для обработки HTTP GET в функции представления будет выглядеть примерно так:

from django.http import HttpResponse

def my_view(request):
    if request.method == 'GET':
        # <view logic>
        return HttpResponse('result')

В представлении на основе класса это станет:

from django.http import HttpResponse
from django.views import View

class MyView(View):
    def get(self, request):
        # <view logic>
        return HttpResponse('result')

Поскольку решатель URL Django ожидает отправки запроса и связанных аргументов в вызываемую функцию, а не в класс, представления на основе классов имеют метод класса as_view(), который возвращает функцию, которую можно вызвать при поступлении запроса на URL, соответствующий связанному шаблону. Функция создает экземпляр класса и вызывает его метод dispatch(). dispatch проверяет запрос, чтобы определить, является ли он GET, POST, и т.д., и передает запрос соответствующему методу, если он определен, или вызывает HttpResponseNotAllowed, если нет:

# urls.py
from django.urls import path
from myapp.views import MyView

urlpatterns = [
    path('about/', MyView.as_view()),
]

Стоит отметить, что то, что возвращает ваш метод, идентично тому, что вы возвращаете из представления на основе функции, а именно некоторой форме HttpResponse. Это означает, что объекты http shortcuts или TemplateResponse допустимы для использования внутри представления на основе класса.

Хотя минимальное представление на основе класса не требует никаких атрибутов класса для выполнения своей работы, атрибуты класса полезны во многих конструкциях на основе класса, и есть два способа настроить или установить атрибуты класса.

Первый — это стандартный способ Python наследования и переопределения атрибутов и методов в подклассе. Так что если ваш родительский класс имеет атрибут greeting вот так:

from django.http import HttpResponse
from django.views import View

class GreetingView(View):
    greeting = "Good Day"

    def get(self, request):
        return HttpResponse(self.greeting)

Вы можете переопределить его в подклассе:

class MorningGreetingView(GreetingView):
    greeting = "Morning to ya"

Другой вариант — настроить атрибуты класса в качестве ключевых аргументов для вызова as_view() в файле URLconf:

urlpatterns = [
    path('about/', GreetingView.as_view(greeting="G'day")),
]

Примечание

Хотя ваш класс инициализируется для каждого запроса, направленного к нему, атрибуты класса, заданные через точку входа as_view(), настраиваются только один раз во время импорта ваших URL.

Использование миксинов

Миксины — это форма множественного наследования, где можно объединить поведение и атрибуты нескольких родительских классов.

Например, в общих представлениях на основе класса есть миксин под названием TemplateResponseMixin, основная цель которого — определить метод render_to_response(). При объединении с поведением базового класса View результатом является класс TemplateView, который будет перенаправлять запросы к соответствующим методам (поведение, определенное в базовом классе View), и который имеет метод render_to_response(), который использует атрибут template_name для возврата объекта TemplateResponse (поведение, определенное в TemplateResponseMixin).

Миксины — отличный способ повторного использования кода в нескольких классах, но они сопряжены с определенной ценой. Чем больше ваш код распределен по миксинам, тем сложнее будет читать дочерний класс и понимать, что именно он делает, и тем сложнее будет понять, какие методы из каких миксинов необходимо переопределить, если вы наследуете от чего-то, что имеет глубокое древо наследования.

Также обратите внимание, что вы можете наследовать только от одного общего представления — то есть только один родительский класс может наследовать от View, а все остальные (если таковые имеются) должны быть миксинами. Попытка наследования от более чем одного класса, наследующего от View — например, попытка использовать форму вверху списка и объединение ProcessFormView и ListView — не сработает должным образом.

Обработка форм с представлениями на основе классов

Базовое представление на основе функции, которое обрабатывает формы, может выглядеть примерно так:

from django.http import HttpResponseRedirect
from django.shortcuts import render

from .forms import MyForm

def myview(request):
    if request.method == "POST":
        form = MyForm(request.POST)
        if form.is_valid():
            # <process form cleaned data>
            return HttpResponseRedirect('/success/')
    else:
        form = MyForm(initial={'key': 'value'})

    return render(request, 'form_template.html', {'form': form})

Аналогичное представление на основе класса может выглядеть так:

from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.views import View

from .forms import MyForm

class MyFormView(View):
    form_class = MyForm
    initial = {'key': 'value'}
    template_name = 'form_template.html'

    def get(self, request, *args, **kwargs):
        form = self.form_class(initial=self.initial)
        return render(request, self.template_name, {'form': form})

    def post(self, request, *args, **kwargs):
        form = self.form_class(request.POST)
        if form.is_valid():
            # <process form cleaned data>
            return HttpResponseRedirect('/success/')

        return render(request, self.template_name, {'form': form})

Это очень простой случай, но вы можете видеть, что у вас будет возможность настроить это представление, переопределяя любые атрибуты класса, например, form_class, через конфигурацию URLconf или создание подкласса и переопределение одного или нескольких методов (или того и другого!).

Декорирование представлений на основе классов

Расширение представлений на основе классов не ограничивается использованием миксинов. Вы также можете использовать декораторы. Поскольку представления на основе классов не являются функциями, декорирование их работает по-разному в зависимости от того, используете ли вы as_view() или создаете подкласс.

Декорирование в URLconf

Самый простой способ декорирования представлений на основе классов — декорировать результат метода as_view(). Самое удобное место для этого — файл URLconf, где вы развертываете своё представление:

from django.contrib.auth.decorators import login_required, permission_required
from django.views.generic import TemplateView

from .views import VoteView

urlpatterns = [
    path('about/', login_required(TemplateView.as_view(template_name="secret.html"))),
    path('vote/', permission_required('polls.can_vote')(VoteView.as_view())),
]

Этот подход применяет декоратор на основе экземпляра. Если вы хотите, чтобы каждый экземпляр представления был декорирован, вам необходимо выбрать другой подход.

Декорирование класса

Для оформления каждого экземпляра класса-представления необходимо оформить само определение класса. Для этого вы применяете декоратор к методу dispatch() класса.

Метод класса не совсем такой же, как автономная функция, поэтому вы не можете просто применить декоратор функции к методу – вам сначала нужно преобразовать его в декоратор метода. Декоратор method_decorator преобразует декоратор функции в декоратор метода, чтобы его можно было использовать для метода экземпляра. Например:

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

class ProtectedView(TemplateView):
    template_name = 'secret.html'

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

Или, более лаконично, вы можете украсить класс вместо этого и передать имя метода, который нужно украсить, в качестве аргумента name:

@method_decorator(login_required, name='dispatch')
class ProtectedView(TemplateView):
    template_name = 'secret.html'

Если у вас есть набор общих декораторов, используемых в нескольких местах, вы можете определить список или кортеж декораторов и использовать его вместо вызова method_decorator() несколько раз. Эти два класса эквивалентны:

decorators = [never_cache, login_required]

@method_decorator(decorators, name='dispatch')
class ProtectedView(TemplateView):
    template_name = 'secret.html'

@method_decorator(never_cache, name='dispatch')
@method_decorator(login_required, name='dispatch')
class ProtectedView(TemplateView):
    template_name = 'secret.html'

Декораторы будут обрабатывать запрос в порядке, в котором они переданы в декоратор. В примере never_cache() обработает запрос до login_required().

В этом примере каждый экземпляр ProtectedView будет иметь защиту от входа.

Примечание

method_decorator передает *args и **kwargs в качестве параметров декорированному методу класса. Если ваш метод не принимает совместимый набор параметров, он вызовет исключение TypeError.

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/2.1/topics/class-based-views/intro/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API