Маршрутизаторы


источник:

  • routers.py


Маршрутизаторы

Маршрутизация ресурсов позволяет быстро объявить все общие маршруты для данного ресурсного контроллера. Вместо объявления отдельных маршрутов для вашего индекса... ресурсный маршрут объявляет их в одной строке кода.

Ruby on Rails Documentation

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

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

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

Вот пример простого URL conf, который использует SimpleRouter.

from rest_framework import routers

router = routers.SimpleRouter()
router.register(r'users', UserViewSet)
router.register(r'accounts', AccountViewSet)
urlpatterns = router.urls

У метода register() есть два обязательных аргумента:

  • prefix - Префикс URL, который будет использоваться для этого набора маршрутов.

  • viewset - Класс набора представлений.

По желанию вы можете указать дополнительный аргумент:

  • basename - Основа, которую следует использовать для создаваемых имен URL. Если значение не задано, то базовое имя будет автоматически генерироваться на основе атрибута queryset набора представлений, если он есть. Обратите внимание, что если набор представлений не включает атрибут queryset, то вы должны установить basename при регистрации набора представлений.

В приведенном выше примере будут сгенерированы следующие шаблоны URL:

  • Шаблон URL: ^users/$ Имя: 'user-list'

  • Шаблон URL: ^users/{pk}/$ Имя: 'user-detail'

  • Шаблон URL: ^accounts/$ Имя: 'account-list'

  • Шаблон URL: ^accounts/{pk}/$ Имя: 'account-detail'


Примечание: Аргумент basename используется для указания начальной части шаблона имени представления. В приведенном выше примере это часть user или account.

Обычно вам не нужно указывать аргумент basename, но если у вас есть набор представлений, в котором вы определили пользовательский метод get_queryset, то набор представлений может не иметь атрибута .queryset. Если вы попытаетесь зарегистрировать этот набор представлений, вы увидите ошибку, подобную этой:

'basename' argument not specified, and could not automatically determine the name from the viewset, as it does not have a '.queryset' attribute.

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


Использование include с маршрутизаторами

Атрибут .urls экземпляра маршрутизатора - это просто стандартный список шаблонов URL. Существует несколько различных стилей для включения этих URL.

Например, вы можете добавить router.urls к списку существующих представлений...

router = routers.SimpleRouter()
router.register(r'users', UserViewSet)
router.register(r'accounts', AccountViewSet)

urlpatterns = [
    path('forgot-password/', ForgotPasswordFormView.as_view()),
]

urlpatterns += router.urls

В качестве альтернативы вы можете использовать функцию Django include, например, так...

urlpatterns = [
    path('forgot-password', ForgotPasswordFormView.as_view()),
    path('', include(router.urls)),
]

Вы можете использовать include с пространством имен приложения:

urlpatterns = [
    path('forgot-password/', ForgotPasswordFormView.as_view()),
    path('api/', include((router.urls, 'app_name'))),
]

Или как пространство имен приложения и экземпляра:

urlpatterns = [
    path('forgot-password/', ForgotPasswordFormView.as_view()),
    path('api/', include((router.urls, 'app_name'), namespace='instance_name')),
]

Более подробную информацию смотрите в документации Django URL namespaces docs и в include API reference.


Примечание: При использовании пространства имен с гиперссылками в сериализаторах вам также необходимо убедиться, что любые параметры view_name в сериализаторах правильно отражают пространство имен. В примерах выше вам нужно будет включить параметр типа view_name='app_name:user-detail' для полей сериализатора, гиперссылка на представление подробных данных пользователя.

Для автоматического создания view_name используется шаблон типа %(имя_модели)-detail. Если только имена ваших моделей не противоречат друг другу, вам, возможно, будет лучше не расставлять имена в представлениях DRF при использовании сериализаторов с гиперссылками.


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

Набор представлений может пометить дополнительные действия для маршрутизации, украсив метод декоратором @action. Эти дополнительные действия будут включены в сгенерированные маршруты. Например, дан метод set_password для класса UserViewSet:

from myapp.permissions import IsAdminOrIsSelf
from rest_framework.decorators import action

class UserViewSet(ModelViewSet):
    ...

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

Будет создан следующий маршрут:

  • Шаблон URL: ^users/{pk}/set_password/$

  • Имя URL: 'user-set-password'.

По умолчанию шаблон URL основан на имени метода, а имя URL представляет собой комбинацию ViewSet.basename и имени метода через дефис. Если вы не хотите использовать значения по умолчанию, вы можете указать аргументы url_path и url_name в декораторе @action.

Например, если вы хотите изменить URL для нашего пользовательского действия на ^users/{pk}/change-password/$, вы можете написать:

from myapp.permissions import IsAdminOrIsSelf
from rest_framework.decorators import action

class UserViewSet(ModelViewSet):
    ...

    @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf],
            url_path='change-password', url_name='change_password')
    def set_password(self, request, pk=None):
        ...

Приведенный выше пример теперь будет генерировать следующий шаблон URL:

  • URL путь: ^users/{pk}/change-password/$

  • Имя URL: 'user-change_password'.

Руководство по API

SimpleRouter

Этот маршрутизатор включает маршруты для стандартного набора действий list, create, retrieve, update, partial_update и destroy. Набор представлений также может отметить дополнительные методы для маршрутизации, используя декоратор @action.

По умолчанию URL, создаваемые SimpleRouter, дополняются косой чертой. Это поведение можно изменить, установив аргумент trailing_slash в False при инстанцировании маршрутизатора. Например:

router = SimpleRouter(trailing_slash=False)

В Django косые черты являются традиционными, но не используются по умолчанию в некоторых других фреймворках, таких как Rails. Какой стиль использовать - это в основном вопрос предпочтений, хотя некоторые javascript-фреймворки могут ожидать определенного стиля маршрутизации.

По умолчанию URL, создаваемые SimpleRouter, используют регулярные выражения. Это поведение можно изменить, установив аргумент use_regex_path в False при инстанцировании маршрутизатора, в этом случае используются преобразователи путей. Например:

router = SimpleRouter(use_regex_path=False)

Примечание: use_regex_path=False работает только с Django 2.x или выше, поскольку эта функция была введена в 2.0.0. Смотрите release note

Маршрутизатор будет сопоставлять значения поиска, содержащие любые символы, кроме косой черты и точки. Чтобы получить более строгий (или мягкий) шаблон поиска, установите атрибут lookup_value_regex для набора представлений или lookup_value_converter при использовании конвертеров путей. Например, вы можете ограничить поиск действительными UUID:

class MyModelViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    lookup_field = 'my_model_id'
    lookup_value_regex = '[0-9a-f]{32}'

class MyPathModelViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    lookup_field = 'my_model_uuid'
    lookup_value_converter = 'uuid'

DefaultRouter

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

Как и в SimpleRouter, косые черты в маршрутах URL могут быть удалены путем установки аргумента trailing_slash в False при инстанцировании маршрутизатора.

router = DefaultRouter(trailing_slash=False)

Пользовательские маршрутизаторы

Реализация пользовательского маршрутизатора - это не то, что вам нужно делать очень часто, но это может быть полезно, если у вас есть особые требования к структуре URL для вашего API. Это позволит вам инкапсулировать структуру URL в многократно используемый способ, который гарантирует, что вам не придется писать шаблоны URL в явном виде для каждого нового представления.

Самый простой способ реализации пользовательского маршрутизатора - это подкласс одного из существующих классов маршрутизаторов. Атрибут .routes используется для шаблонизации шаблонов URL, которые будут сопоставлены с каждым набором представлений. Атрибут .routes представляет собой список кортежей с именем Route.

Аргументами кортежа с именем Route являются:

url: Строка, представляющая URL, который должен быть маршрутизирован. Может включать следующие строки формата:

  • {prefix} - Префикс URL, который будет использоваться для этого набора маршрутов.

  • {lookup} - Поле поиска, используемое для сопоставления с одним экземпляром.

  • {trailing_slash} - Либо '/', либо пустая строка, в зависимости от аргумента trailing_slash.

mapping: Сопоставление имен методов HTTP с методами представления

name: Имя URL, используемое в вызовах reverse. Может включать следующую строку формата:

  • {basename} - Основа, которую следует использовать для создаваемых имен URL.

initkwargs: Словарь дополнительных аргументов, которые должны быть переданы при инстанцировании представления. Обратите внимание, что аргументы detail, basename и suffix зарезервированы для интроспекции набора представлений и также используются API просмотра для генерации имени представления и ссылок на хлебные крошки.

Настройка динамических маршрутов

Вы также можете настроить способ маршрутизации декоратора @action. Включите кортеж с именем DynamicRoute в список .routes, установив аргумент detail в соответствии с требованиями для маршрутов на основе списка и на основе деталей. В дополнение к detail, аргументами DynamicRoute являются:

url: Строка, представляющая URL, который должен быть маршрутизирован. Может включать те же строки формата, что и Route, и дополнительно принимает строку формата {url_path}.

name: Имя URL, используемое в вызовах reverse. Может включать следующие строки формата:

  • {basename} - Основа, которую следует использовать для создаваемых имен URL.

  • {url_name} - имя URL, предоставляемое @action.

initkwargs: Словарь любых дополнительных аргументов, которые должны быть переданы при инстанцировании представления.

Пример

Следующий пример маршрутизирует только действия list и retrieve и не использует соглашение о косой черте.

from rest_framework.routers import Route, DynamicRoute, SimpleRouter

class CustomReadOnlyRouter(SimpleRouter):
    """
    A router for read-only APIs, which doesn't use trailing slashes.
    """
    routes = [
        Route(
            url=r'^{prefix}$',
            mapping={'get': 'list'},
            name='{basename}-list',
            detail=False,
            initkwargs={'suffix': 'List'}
        ),
        Route(
            url=r'^{prefix}/{lookup}$',
            mapping={'get': 'retrieve'},
            name='{basename}-detail',
            detail=True,
            initkwargs={'suffix': 'Detail'}
        ),
        DynamicRoute(
            url=r'^{prefix}/{lookup}/{url_path}$',
            name='{basename}-{url_name}',
            detail=True,
            initkwargs={}
        )
    ]

Давайте посмотрим на маршруты, которые наш CustomReadOnlyRouter будет генерировать для простого набора представлений.

views.py:

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

    @action(detail=True)
    def group_names(self, request, pk=None):
        """
        Returns a list of all the group names that the given
        user belongs to.
        """
        user = self.get_object()
        groups = user.groups.all()
        return Response([group.name for group in groups])

urls.py:

router = CustomReadOnlyRouter()
router.register('users', UserViewSet)
urlpatterns = router.urls

Будут созданы следующие отображения...

Другой пример установки атрибута .routes приведен в исходном коде класса SimpleRouter.

Расширенные пользовательские маршрутизаторы

Если вы хотите обеспечить полностью пользовательское поведение, вы можете переопределить BaseRouter и переопределить метод get_urls(self). Метод должен проверить зарегистрированные наборы представлений и вернуть список шаблонов URL. Зарегистрированные кортежи префикса, набора представлений и базового имени можно проверить, обратившись к атрибуту self.registry.

Вы также можете переопределить метод get_default_basename(self, viewset) или всегда явно задавать аргумент basename при регистрации ваших наборов представлений в маршрутизаторе.

Сторонние пакеты

Также доступны следующие пакеты сторонних производителей.

DRF Nested Routers

Пакет drf-nested-routers предоставляет маршрутизаторы и поля отношений для работы с вложенными ресурсами.

ModelRouter (wq.db.rest)

Пакет wq.db предоставляет расширенный класс ModelRouter (и экземпляр синглтона), который расширяет DefaultRouter с API register_model(). Подобно Django's admin.site.register, единственным необходимым аргументом для rest.router.register_model является класс модели. Разумные значения по умолчанию для префикса url, сериализатора и набора представлений будут определяться из модели и глобальной конфигурации.

from wq.db import rest
from myapp.models import MyModel

rest.router.register_model(MyModel)

DRF-extensions

Пакет DRF-extensions предоставляет маршрутизаторы для создания вложенных наборов представлений, контроллеров уровня коллекции с настраиваемыми именами конечных точек.

Last updated