Spec-Zone.ru › Django 2.0

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

Представления на основе классов предоставляют альтернативный способ реализации представлений как объектов 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.0/topics/class-based-views/intro/

Spec-Zone.ru

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