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

***

источник:

* routers.py

***

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

> Маршрутизация ресурсов позволяет быстро объявить все общие маршруты для данного ресурсного контроллера. Вместо объявления отдельных маршрутов для вашего индекса... ресурсный маршрут объявляет их в одной строке кода.
>
> — [Ruby on Rails Documentation](https://guides.rubyonrails.org/routing.html)

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

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

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

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

```python
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` к списку существующих представлений...

```python
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`, например, так...

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

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

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

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

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

Более подробную информацию смотрите в документации Django [URL namespaces docs](https://docs.djangoproject.com/en/4.0/topics/http/urls/#url-namespaces) и в [`include` API reference](https://docs.djangoproject.com/en/4.0/ref/urls/#include).

***

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

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

***

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

Набор представлений может [пометить дополнительные действия для маршрутизации](https://ilyachch.gitbook.io/django-rest-framework-russian-documentation/overview/navigaciya-po-api/pages/-LegWRmnuxfT_M8K1LgL#добавление-дополнительных-действий-в-маршрутизацию), украсив метод декоратором `@action`. Эти дополнительные действия будут включены в сгенерированные маршруты. Например, дан метод `set_password` для класса `UserViewSet`:

```python
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/$`, вы можете написать:

```python
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 Style                      | HTTP Method                                | Action                                      | URL Name               |
| ------------------------------ | ------------------------------------------ | ------------------------------------------- | ---------------------- |
| {prefix}/                      | GET                                        | list                                        | {basename}-list        |
|                                | POST                                       | create                                      |                        |
| {prefix}/{url\_path}/          | GET, или как указано в аргументе `methods` | метод с декоратором `@action(detail=False)` | {basename}-{url\_name} |
| {prefix}/{lookup}/             | GET                                        | retrieve                                    | {basename}-detail      |
|                                | PUT                                        | update                                      |                        |
|                                | PATCH                                      | partial\_update                             |                        |
|                                | DELETE                                     | destroy                                     |                        |
| {prefix}/{lookup}/{url\_path}/ | GET, или как указано в аргументе `methods` | метод с декоратором `@action(detail=True)`  | {basename}-{url\_name} |

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

```python
router = SimpleRouter(trailing_slash=False)
```

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

По умолчанию URL, создаваемые `SimpleRouter`, используют регулярные выражения. Это поведение можно изменить, установив аргумент `use_regex_path` в `False` при инстанцировании маршрутизатора, в этом случае используются [преобразователи путей](https://docs.djangoproject.com/en/2.0/topics/http/urls/#path-converters). Например:

```python
router = SimpleRouter(use_regex_path=False)
```

**Примечание**: `use_regex_path=False` работает только с Django 2.x или выше, поскольку эта функция была введена в 2.0.0. Смотрите [release note](https://docs.djangoproject.com/en/2.0/releases/2.0/#simplified-url-routing-syntax)

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

```python
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`.

| URL Style                                | HTTP Method                                | Action                                               | URL Name               |
| ---------------------------------------- | ------------------------------------------ | ---------------------------------------------------- | ---------------------- |
| \[.format]                               | GET                                        | автоматически сгенерированное корневое представление | api-root               |
| {prefix}/\[.format]                      | GET                                        | list                                                 | {basename}-list        |
|                                          | POST                                       | create                                               |                        |
| {prefix}/{url\_path}/\[.format]          | GET, или как указано в аргументе `methods` | метод с декоратором `@action(detail=False)`          | {basename}-{url\_name} |
| {prefix}/{lookup}/\[.format]             | GET                                        | retrieve                                             | {basename}-detail      |
|                                          | PUT                                        | update                                               |                        |
|                                          | PATCH                                      | partial\_update                                      |                        |
|                                          | DELETE                                     | destroy                                              |                        |
| {prefix}/{lookup}/{url\_path}/\[.format] | GET, или как указано в аргументе `methods` | метод с декоратором `@action(detail=True)`           | {basename}-{url\_name} |

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

```python
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` и не использует соглашение о косой черте.

```python
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`:

```python
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`:

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

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

| URL                            | HTTP Method | Action       | URL Name         |
| ------------------------------ | ----------- | ------------ | ---------------- |
| /users                         | GET         | list         | user-list        |
| /users/{username}              | GET         | retrieve     | user-detail      |
| /users/{username}/group\_names | GET         | group\_names | user-group-names |

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

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

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

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

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

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

### DRF Nested Routers

Пакет [drf-nested-routers](https://github.com/alanjds/drf-nested-routers) предоставляет маршрутизаторы и поля отношений для работы с вложенными ресурсами.

### ModelRouter (wq.db.rest)

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

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

rest.router.register_model(MyModel)
```

### DRF-extensions

Пакет [`DRF-extensions`](https://chibisov.github.io/drf-extensions/docs/) предоставляет [маршрутизаторы](https://chibisov.github.io/drf-extensions/docs/#routers) для создания [вложенных наборов представлений](https://chibisov.github.io/drf-extensions/docs/#nested-routes), [контроллеров уровня коллекции](https://chibisov.github.io/drf-extensions/docs/#collection-level-controllers) с [настраиваемыми именами конечных точек](https://chibisov.github.io/drf-extensions/docs/#controller-endpoint-name).


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ilyachch.gitbook.io/django-rest-framework-russian-documentation/overview/navigaciya-po-api/routers.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
