Spec-Zone.ru › Django 5.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, соответствующий связанному шаблону. Функция создает экземпляр класса, вызывает 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.0/topics/class-based-views/intro/

Spec-Zone.ru

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