Spec-Zone.ru › Django REST Framework

Наборы представлений

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

— Документация Ruby on Rails

Django REST framework позволяет комбинировать логику набора связанных представлений в одном классе, называемом ViewSet. В других фреймворках вы также можете найти концептуально похожие реализации, называемые чем-то вроде «Ресурсы» или «Контроллеры».

Класс ViewSet - это просто тип представления на основе класса, который не предоставляет обработчики методов, таких как .get() или .post(), а вместо этого предоставляет действия, такие как .list() и .create().

Обработчики методов для ViewSet привязываются к соответствующим действиям только на этапе завершения представления, используя метод .as_view().

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

Пример

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

from django.contrib.auth.models import User
from django.shortcuts import get_object_or_404
from myapps.serializers import UserSerializer
from rest_framework import viewsets
from rest_framework.response import Response

class UserViewSet(viewsets.ViewSet):
    """
    A simple ViewSet for listing or retrieving users.
    """
    def list(self, request):
        queryset = User.objects.all()
        serializer = UserSerializer(queryset, many=True)
        return Response(serializer.data)

    def retrieve(self, request, pk=None):
        queryset = User.objects.all()
        user = get_object_or_404(queryset, pk=pk)
        serializer = UserSerializer(user)
        return Response(serializer.data)

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

user_list = UserViewSet.as_view({'get': 'list'})
user_detail = UserViewSet.as_view({'get': 'retrieve'})

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

from myapp.views import UserViewSet
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r'users', UserViewSet, basename='user')
urlpatterns = router.urls

Вместо написания собственных наборов представлений, вы часто захотите использовать существующие базовые классы, которые предоставляют набор поведения по умолчанию. Например:

class UserViewSet(viewsets.ModelViewSet):
    """
    A viewset for viewing and editing user instances.
    """
    serializer_class = UserSerializer
    queryset = User.objects.all()

Есть два основных преимущества использования класса ViewSet по сравнению с использованием класса View.

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

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

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

Роутеры по умолчанию, включенные в REST фреймворк, предоставят маршруты для стандартного набора действий типа «создание/получение/обновление/удаление», как показано ниже:

class UserViewSet(viewsets.ViewSet):
    """
    Example empty viewset demonstrating the standard
    actions that will be handled by a router class.

    If you're using format suffixes, make sure to also include
    the `format=None` keyword argument for each action.
    """

    def list(self, request):
        pass

    def create(self, request):
        pass

    def retrieve(self, request, pk=None):
        pass

    def update(self, request, pk=None):
        pass

    def partial_update(self, request, pk=None):
        pass

    def destroy(self, request, pk=None):
        pass

Просмотр действий набора представлений

Во время диспетчеризации следующие атрибуты доступны для ViewSet.

  • basename - база для использования имен URL, которые создаются.
  • action - имя текущего действия (например, list, create).
  • detail - булево значение, указывающее, настроено ли текущее действие для представления списка или детали.
  • suffix - суффикс отображения для типа набора представлений - отражает атрибут detail.
  • name - отображаемое имя набора представлений. Этот аргумент взаимоисключает suffix.
  • description - отображаемое описание для отдельного представления набора представлений.

Вы можете проверить эти атрибуты, чтобы скорректировать поведение в зависимости от текущего действия. Например, вы можете ограничить разрешения на всё, кроме действия list аналогично этому:

def get_permissions(self):
    """
    Instantiates and returns the list of permissions that this view requires.
    """
    if self.action == 'list':
        permission_classes = [IsAuthenticated]
    else:
        permission_classes = [IsAdminUser]
    return [permission() for permission in permission_classes]

Отмечание дополнительных действий для маршрутизации

Если у вас есть методы ad-hoc, которые должны быть маршрутизируемыми, вы можете отметить их как таковые с помощью декоратора @action. Как и обычные действия, дополнительные действия могут быть предназначены для одного объекта или целого набора. Для указания этого установите аргумент detail в True или False. Роутер соответственно сконфигурирует свои шаблоны URL. Например, DefaultRouter сконфигурирует действия детали, чтобы они содержали pk в своих шаблонах URL.

Более полный пример дополнительных действий:

from django.contrib.auth.models import User
from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from myapp.serializers import UserSerializer, PasswordSerializer

class UserViewSet(viewsets.ModelViewSet):
    """
    A viewset that provides the standard actions
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer

    @action(detail=True, methods=['post'])
    def set_password(self, request, pk=None):
        user = self.get_object()
        serializer = PasswordSerializer(data=request.data)
        if serializer.is_valid():
            user.set_password(serializer.validated_data['password'])
            user.save()
            return Response({'status': 'password set'})
        else:
            return Response(serializer.errors,
                            status=status.HTTP_400_BAD_REQUEST)

    @action(detail=False)
    def recent_users(self, request):
        recent_users = User.objects.all().order_by('-last_login')

        page = self.paginate_queryset(recent_users)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(recent_users, many=True)
        return Response(serializer.data)

Декоратор action по умолчанию маршрутизирует запросы GET, но также может принимать и другие HTTP-методы, установив аргумент methods. Например:

    @action(detail=True, methods=['post', 'delete'])
    def unset_password(self, request, pk=None):
       ...

Аргумент methods также поддерживает HTTP-методы, определенные как HTTPMethod. Пример ниже идентичен предыдущему:

    from http import HTTPMethod

    @action(detail=True, methods=[HTTPMethod.POST, HTTPMethod.DELETE])
    def unset_password(self, request, pk=None):
       ...

Декоратор позволяет переопределить любую конфигурацию на уровне набора представлений, такую как permission_classes, serializer_class, filter_backends…:

    @action(detail=True, methods=['post'], permission_classes=[IsAdminOrIsSelf])
    def set_password(self, request, pk=None):
       ...

Два новых действия затем будут доступны по URL ^users/{pk}/set_password/$ и ^users/{pk}/unset_password/$. Используйте параметры url_path и url_name для изменения сегмента URL и имени обратного URL действия.

Для просмотра всех дополнительных действий вызовите метод .get_extra_actions().

Маршрутизация дополнительных HTTP-методов для дополнительных действий

Дополнительные действия могут сопоставлять дополнительные HTTP-методы с отдельными методами ViewSet. Например, методы установки/снятия пароля, описанные выше, можно объединить в один маршрут. Обратите внимание, что дополнительные сопоставления не принимают аргументы.

@action(detail=True, methods=["put"], name="Change Password")
def password(self, request, pk=None):
    """Update the user's password."""
    ...


@password.mapping.delete
def delete_password(self, request, pk=None):
    """Delete the user's password."""
    ...

Обращение URL-адресов действий

Если вам нужно получить URL-адрес действия, используйте метод .reverse_action(). Это обертка для reverse(), автоматически передающая объект представления request и добавляющая префикс url_name с атрибутом .basename.

Обратите внимание, что basename предоставляется роутером во время регистрации ViewSet. Если вы не используете роутер, то вы должны предоставить аргумент basename методу .as_view().

Используя пример из предыдущего раздела:

>>> view.reverse_action("set-password", args=["1"])
'http://localhost:8000/api/users/1/set_password'

В качестве альтернативы можно использовать атрибут url_name, установленный декоратором @action.

>>> view.reverse_action(view.set_password.url_name, args=['1'])
'http://localhost:8000/api/users/1/set_password'

Аргумент url_name для .reverse_action() должен совпадать с тем же аргументом декоратора @action. Кроме того, этот метод может быть использован для обратного обращения к стандартным действиям, таким как list и create.

Справочник API

Набор представлений

Класс ViewSet наследуется от APIView. Вы можете использовать любые стандартные атрибуты, такие как permission_classes, authentication_classes, чтобы управлять политикой API набора представлений.

Класс ViewSet не предоставляет никаких реализаций действий. Для использования класса ViewSet вы переопределите класс и явно определите реализации действий.

Общий набор представлений

Класс GenericViewSet наследуется от GenericAPIView, и предоставляет набор стандартных методов get_object, get_queryset и другое поведение базового представления, но по умолчанию не включает никаких действий.

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

Модель-набор представлений

Класс ModelViewSet наследуется от GenericAPIView и включает реализации различных действий, комбинируя поведение различных миксин-классов.

Действия, предоставляемые классом ModelViewSet: .list(), .retrieve(), .create(), .update(), .partial_update(), и .destroy().

Пример

Поскольку ModelViewSet расширяет GenericAPIView, обычно вам нужно указать как минимум атрибуты queryset и serializer_class.

Например:

class AccountViewSet(viewsets.ModelViewSet):
    """
    A simple ViewSet for viewing and editing accounts.
    """
    queryset = Account.objects.all()
    serializer_class = AccountSerializer
    permission_classes = [IsAccountAdminOrReadOnly]

Обратите внимание, что вы можете использовать любые стандартные атрибуты или переопределения методов, предоставляемые GenericAPIView. Например, для использования ViewSet который динамически определяет набор запросов, который он должен обрабатывать, вы можете сделать следующее:

class AccountViewSet(viewsets.ModelViewSet):
    """
    A simple ViewSet for viewing and editing the accounts
    associated with the user.
    """
    serializer_class = AccountSerializer
    permission_classes = [IsAccountAdminOrReadOnly]

    def get_queryset(self):
        return self.request.user.accounts.all()

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

Также обратите внимание, что хотя этот класс предоставляет полный набор действий «создание/список/получение/обновление/удаление» по умолчанию, вы можете ограничить доступные операции, используя стандартные классы разрешений.

Только-чтение набор представлений модели

Класс ReadOnlyModelViewSet также наследуется от GenericAPIView. Как и ModelViewSet, он также включает реализации различных действий, но в отличие от ModelViewSet, предоставляет только действия «только для чтения», .list() и .retrieve().

Пример

Как и в случае с ModelViewSet, вам обычно нужно указать по крайней мере атрибуты queryset и serializer_class. Например:

class AccountViewSet(viewsets.ReadOnlyModelViewSet):
    """
    A simple ViewSet for viewing accounts.
    """
    queryset = Account.objects.all()
    serializer_class = AccountSerializer

Снова, как и в ModelViewSet, вы можете использовать любые стандартные атрибуты и переопределения методов, доступные для GenericAPIView.

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

Возможно, вам потребуется предоставить пользовательские классы ViewSet, которые не имеют полного набора действий ModelViewSet, или которые кастомизируют поведение каким-либо другим способом.

Пример

Чтобы создать базовый класс набора представлений, предоставляющий операции create, list и retrieve, наследуйтесь от GenericViewSet и примените необходимые действия:

from rest_framework import mixins, viewsets

class CreateListRetrieveViewSet(mixins.CreateModelMixin,
                                mixins.ListModelMixin,
                                mixins.RetrieveModelMixin,
                                viewsets.GenericViewSet):
    """
    A viewset that provides `retrieve`, `create`, and `list` actions.

    To use it, override the class and set the `.queryset` and
    `.serializer_class` attributes.
    """
    pass

Создавая собственные базовые классы ViewSet, вы можете предоставить общее поведение, которое может быть повторно использовано в нескольких наборах представлений по всему вашему API.

viewsets.py

Copyright © 2011–present Encode OSS Ltd.
Licensed under the BSD License.
https://www.django-rest-framework.org/api-guide/viewsets/

Spec-Zone.ru

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