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

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

Ruby on Rails Documentation

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

Фреймворк REST добавляет в 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')),
]

Более подробно смотрите URL namespaces docs и ** ``include:doc:` API reference <https://docs.djangoproject.com/en/2.0/ref/urls/#include>` в Django.


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

Автоматическая генерация view_name использует шаблон типа %(model_name)-detail. Если только имена ваших моделей не противоречат друг другу, вам лучше ***не **расставлять имена в представлениях Django REST Framework при использовании сериализаторов с гиперссылками.


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

Набор представлений может mark extra actions for routing, украсив метод декоратором @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 StyleHTTP MethodActionURL Name
{prefix}/GETlist{basename}-list
POSTcreate
{prefix}/{url_path}/GET, or as specified by `methods` argument`@action(detail=False)` decorated method{basename}-{url_name}
{prefix}/{lookup}/GETretrieve{basename}-detail
PUTupdate
PATCHpartial_update
DELETEdestroy
{prefix}/{lookup}/{url_path}/GET, or as specified by `methods` argument`@action(detail=True)` decorated method{basename}-{url_name}

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

router = SimpleRouter(trailing_slash=False)

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

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

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

DefaultRouter

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

URL StyleHTTP MethodActionURL Name
[.format]GETautomatically generated root viewapi-root
{prefix}/[.format]GETlist{basename}-list
POSTcreate
{prefix}/{url_path}/[.format]GET, or as specified by `methods` argument`@action(detail=False)` decorated method{basename}-{url_name}
{prefix}/{lookup}/[.format]GETretrieve{basename}-detail
PUTupdate
PATCHpartial_update
DELETEdestroy
{prefix}/{lookup}/{url_path}/[.format]GET, or as specified by `methods` argument`@action(detail=True)` decorated method{basename}-{url_name}

Как и в случае с 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_name, предоставляемый @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

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

URLHTTP MethodActionURL Name
/usersGETlistuser-list
/users/{username}GETretrieveuser-detail
/users/{username}/group_namesGETgroup_namesuser-group-names

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

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

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

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

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

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

Вложенные маршрутизаторы DRF

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

ModelRouter (wq.db.rest)

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

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

rest.router.register_model(MyModel)

DRF-расширения

** ``DRF-extensions:doc:` package <https://chibisov.github.io/drf-extensions/docs/>` обеспечивает routers для создания nested viewsets с customizable endpoint names.

Вернуться на верх