Spec-Zone.ru › Django 5.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, соответствующий связанному шаблону. Функция создаёт экземпляр класса, вызывает setup() для инициализации своих атрибутов и затем вызывает свой метод 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 будет иметь защиту от входа. В этих примерах используется login_required, однако аналогичного поведения можно достичь, используя LoginRequiredMixin.

Примечание

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

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

Spec-Zone.ru

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