# Schemas

> Машиночитаемая \[схема] описывает, какие ресурсы доступны через API, каковы их URL, как они представлены и какие операции они поддерживают.
>
> * Heroku, [JSON Schema for the Heroku Platform API](https://blog.heroku.com/archives/2014/1/8/json_schema_for_heroku_platform_api)

***

**Уведомление о сокращении:**

Встроенная в DRF поддержка генерации схем OpenAPI **утрачена** в пользу сторонних пакетов, которые могут предоставить эту функциональность вместо нее. Встроенная поддержка будет перенесена в отдельный пакет, а затем в последующих релизах будет удалена.

В качестве полноценной замены мы рекомендуем пакет [drf-spectacular](https://drf-spectacular.readthedocs.io/en/latest/readme.html). Он обладает широкой поддержкой генерации схем OpenAPI 3 из API DRF, причем доступны как автоматические, так и настраиваемые опции. За дополнительной информацией обращайтесь к [Documenting your API](https://ilyachch.gitbook.io/django-rest-framework-russian-documentation/stati/topics/documenting-your-api#drf-spectacular).

***

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

DRF обеспечивает поддержку автоматической генерации схем [OpenAPI](https://github.com/OAI/OpenAPI-Specification).

## Обзор

Генерация схемы состоит из нескольких движущихся частей. Стоит сделать обзор:

* `SchemaGenerator` - это класс верхнего уровня, который отвечает за поиск шаблонов URL, нахождение подклассов `APIView`, запрос их представления схемы и компиляцию конечного объекта схемы.
* `AutoSchema` инкапсулирует все детали, необходимые для интроспекции схемы для каждого представления. Прикрепляется к каждому представлению через атрибут `schema`. Вы подклассифицируете `AutoSchema` для того, чтобы настроить свою схему.
* Команда управления `generateschema` позволяет вам генерировать статическую схему в автономном режиме.
* Альтернативно, вы можете направить `SchemaView` для динамической генерации и обслуживания вашей схемы.
* `settings.DEFAULT_SCHEMA_CLASS` позволяет вам указать подкласс `AutoSchema`, который будет использоваться по умолчанию в вашем проекте.

В следующих разделах рассказывается подробнее.

## Генерация схемы OpenAPI

### Установите зависимости

```bash
pip install pyyaml uritemplate inflection
```

* `pyyaml` используется для генерации схемы в формат OpenAPI на основе YAML.
* `uritemplate` используется для получения параметров в пути.
* `inflection` для более подходящего множественного числа операций в конечных точках списка.

### Генерация статической схемы с помощью команды управления `generateschema`.

Если ваша схема статична, вы можете использовать команду управления `generateschema`:

```bash
./manage.py generateschema --file openapi-schema.yml
```

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

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

### Генерация динамической схемы с помощью `SchemaView`.

Если вам нужна динамическая схема, например, потому что выбор внешнего ключа зависит от значений базы данных, вы можете создать `SchemaView`, который будет генерировать и обслуживать вашу схему по требованию.

Для маршрутизации `SchemaView` используйте помощник `get_schema_view()`.

В `urls.py`:

```python
from rest_framework.schemas import get_schema_view

urlpatterns = [
    # ...
    # Use the `get_schema_view()` helper to add a `SchemaView` to project URLs.
    #   * `title` and `description` parameters are passed to `SchemaGenerator`.
    #   * Provide view name for use with `reverse()`.
    path(
        "openapi",
        get_schema_view(
            title="Your Project", description="API for all things …", version="1.0.0"
        ),
        name="openapi-schema",
    ),
    # ...
]
```

#### `get_schema_view()`.

Помощник `get_schema_view()` принимает следующие именованные аргументы:

* `title`: Может использоваться для предоставления описательного заголовка для определения схемы.
* `description`: Более длинный описательный текст.
* `version`: Версия API.
* `url`: Может использоваться для передачи канонического базового URL для схемы.

  ```python
  schema_view = get_schema_view(
      title='API мониторинга серверов',
      url='https://www.example.org/api/'
  )
  ```
* `urlconf`: Строка, представляющая путь импорта к URL conf, для которого вы хотите сгенерировать схему API. По умолчанию это значение соответствует значению параметра Django `ROOT_URLCONF`.

  ```python
  schema_view = get_schema_view(
      title='API мониторинга серверов',
      url='https://www.example.org/api/',
      urlconf='myproject.urls'
  )
  ```
* `patterns`: Список шаблонов url для ограничения интроспекции схемы. Если вам хотите, чтобы в схеме отображались только урлы `myproject.api`:

  ```python
  schema_url_patterns = [
      path('api/', include('myproject.api.urls')),
  ]

  schema_view = get_schema_view(
      title='API мониторинга серверов',
      url='https://www.example.org/api/',
      patterns=schema_url_patterns,
  )
  ```
* `public`: Может использоваться для указания того, должна ли схема обходить разрешения представления. По умолчанию False
* `generator_class`: Может использоваться для указания подкласса `SchemaGenerator`, который будет передаваться в `SchemaView`.
* `authentication_classes`: Может использоваться для указания списка классов аутентификации классов, которые будут применяться к конечной точке схемы. По умолчанию `settings.DEFAULT_AUTHENTICATION_CLASSES`.
* `permission_classes`: Может использоваться для указания списка классов разрешений. которые будут применяться к конечной точке схемы. По умолчанию `settings.DEFAULT_PERMISSION_CLASSES`.
* `renderer_classes`: Может использоваться для передачи набора классов рендереров, которые могут использоваться для рендеринга корневой конечной точки API.

## SchemaGenerator

**Настройка на уровне схемы**

```python
from rest_framework.schemas.openapi import SchemaGenerator
```

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

Обычно вам не нужно самостоятельно создавать `SchemaGenerator`, но вы можете сделать это следующим образом:

```python
generator = SchemaGenerator(title='Stock Prices API')
```

Аргументы:

* `title` **обязательно**: Название API.
* `description`: Более длинный описательный текст.
* `version`: Версия API. По умолчанию `0.1.0`.
* `url`: Корневой URL схемы API. Этот параметр не требуется, если схема не включена в префикс path.
* `patterns`: Список URL-адресов для проверки при генерации схемы. По умолчанию используется URL conf проекта.
* `urlconf`: Имя модуля URL conf для использования при генерации схемы. По умолчанию `settings.ROOT_URLCONF`.

Чтобы настроить схему верхнего уровня, подкласс `rest_framework.schemas.openapi.SchemaGenerator` и предоставьте свой подкласс в качестве аргумента команде `generateschema` или вспомогательной функции `get_schema_view()`.

### get\_schema(self, request=None, public=False)

Возвращает словарь, представляющий схему OpenAPI:

```python
generator = SchemaGenerator(title='Stock Prices API')
schema = generator.get_schema()
```

Аргумент `request` является необязательным и может быть использован, если вы хотите применить разрешения для каждого пользователя к результирующей генерации схемы.

Например, вы можете добавить условия обслуживания в [объект верхнего уровня `info`](https://swagger.io/specification/#infoObject):

```python
class TOSSchemaGenerator(SchemaGenerator):
    def get_schema(self, *args, **kwargs):
        schema = super().get_schema(*args, **kwargs)
        schema["info"]["termsOfService"] = "https://example.com/tos.html"
        return schema
```

## AutoSchema

**Настройка для каждого представления**

```python
from rest_framework.schemas.openapi import AutoSchema
```

По умолчанию интроспекция представления выполняется экземпляром `AutoSchema`, доступным через атрибут `schema` на `APIView`.

```python
auto_schema = some_view.schema
```

`AutoSchema` предоставляет элементы OpenAPI, необходимые для каждого представления, метода запроса и пути:

* Список [компонентов OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#componentsObject). В терминах DRF это отображения сериализаторов, которые описывают тела запроса и ответа.
* Соответствующий [объект операции OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#operationObject), описывающий конечную точку, включая путь и параметры запроса для пагинации, фильтрации и так далее.

```python
components = auto_schema.get_components(...)
operation = auto_schema.get_operation(...)
```

При компиляции схемы `SchemaGenerator` вызывает `get_components()` и `get_operation()` для каждого представления, разрешенного метода и пути.

***

**Примечание**: Автоматическая интроспекция компонентов и многих параметров операций опирается на соответствующие атрибуты и методы `GenericAPIView`: `get_serializer()`, `pagination_class`, `filter_backends` и т.д. По этой причине для базовых подклассов `APIView` интроспекция по умолчанию ограничивается именованными параметрами пути URL.

***

`AutoSchema` инкапсулирует интроспекцию представления, необходимую для генерации схемы. Благодаря этому вся логика генерации схемы хранится в одном месте, а не распределяется по уже существующим API представления, сериализатора и полей.

Следуя этому шаблону, старайтесь не допускать утечки логики схемы в ваши собственные представления, сериализаторы или поля при настройке генерации схемы. У вас может возникнуть соблазн сделать что-то вроде этого:

```python
class CustomSchema(AutoSchema):
    """
    AutoSchema subclass using schema_extra_info on the view.
    """

    ...


class CustomView(APIView):
    schema = CustomSchema()
    schema_extra_info = ...  # some extra info
```

Здесь подкласс `AutoSchema` ищет `schema_extra_info` в представлении. Это *OK* (на самом деле это не вредит), но это означает, что в итоге вы получите логику схемы, разбросанную по разным местам.

Вместо этого попробуйте подкласс `AutoSchema`, чтобы `extra_info` не просачивалась в представление:

```python
class BaseSchema(AutoSchema):
    """
    AutoSchema subclass that knows how to use extra_info.
    """

    ...


class CustomSchema(BaseSchema):
    extra_info = ...  # some extra info


class CustomView(APIView):
    schema = CustomSchema()
```

Этот стиль немного более многословен, но сохраняет инкапсуляцию кода, связанного со схемой. Он более *целостный* в *парламенте*. Это сделает остальной код вашего API более аккуратным.

Если опция применяется ко многим классам представлений, вместо того, чтобы создавать отдельный подкласс для каждого представления, вам может показаться более удобным разрешить указывать опцию как `__init__()` именованный аргумент для вашего базового подкласса `AutoSchema`:

```python
class CustomSchema(BaseSchema):
    def __init__(self, **kwargs):
        # store extra_info for later
        self.extra_info = kwargs.pop("extra_info")
        super().__init__(**kwargs)


class CustomView(APIView):
    schema = CustomSchema(extra_info=...)  # some extra info
```

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

Не все методы `AutoSchema` раскрывают соответствующие ключи `__init__()`, но для наиболее часто используемых опций они есть.

### Методы `AutoSchema`

#### `get_components()`.

Генерирует компоненты OpenAPI, описывающие тела запросов и ответов, получая их свойства от сериализатора.

Возвращает словарь, отображающий имя компонента на сгенерированное представление. По умолчанию он содержит только одну пару, но вы можете переопределить `get_components()`, чтобы вернуть несколько пар, если ваше представление использует несколько сериализаторов.

#### `get_component_name()`.

Вычисляет имя компонента из сериализатора.

Вы можете увидеть предупреждения, если в вашем API есть дублирующиеся имена компонентов. В этом случае вы можете переопределить `get_component_name()` или передать `component_name` `__init__()` именованный аргумент (см. ниже), чтобы обеспечить разные имена.

#### `get_reference()`.

Возвращает ссылку на компонент сериализатора. Это может быть полезно, если вы переопределите `get_schema()`.

#### `map_serializer()`.

Сопоставляет сериализаторы с их представлениями OpenAPI.

Большинство сериализаторов должны соответствовать стандартному типу OpenAPI `object`, но вы можете переопределить `map_serializer()`, чтобы настроить это или другие поля на уровне сериализатора.

#### `map_field()`.

Сопоставляет отдельные поля сериализатора с их схемным представлением. Базовая реализация будет работать с полями по умолчанию, которые предоставляет DRF.

Для экземпляров `SerializerMethodField`, для которых схема неизвестна, или подклассов пользовательских полей следует переопределить `map_field()`, чтобы сгенерировать правильную схему:

```python
class CustomSchema(AutoSchema):
    """Extension of ``AutoSchema`` to add support for custom field schemas."""

    def map_field(self, field):
        # Handle SerializerMethodFields or custom fields here...
        # ...
        return super().map_field(field)
```

Авторы сторонних пакетов должны стремиться предоставить подкласс `AutoSchema` и миксин, переопределяющий `map_field()`, чтобы пользователи могли легко генерировать схемы для своих пользовательских полей.

#### `get_tags()`.

OpenAPI группирует операции по тегам. По умолчанию теги берутся из первого сегмента пути маршрутизируемого URL. Например, URL типа `/users/{id}/` будет генерировать тег `users`.

Вы можете передать именованный аргумент в `__init__()`, чтобы вручную указать теги (см. ниже), или переопределить `get_tags()`, чтобы обеспечить пользовательскую логику.

#### `get_operation()`.

Возвращает [объект операции OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#operationObject), описывающий конечную точку, включая путь и параметры запроса для пагинации, фильтрации и так далее.

Вместе с `get_components()` это основная точка входа в интроспекцию представления.

#### `get_operation_id()`.

Для каждой операции должен быть уникальный `operationid`. По умолчанию `operationId` выводится из имени модели, имени сериализатора или имени представления. OperationId выглядит как "listItems", "retrieveItem", "updateItem" и т.д. По соглашению `operationId` используется camelCase.

#### `get_operation_id_base()`.

Если у вас есть несколько представлений с одинаковым именем модели, вы можете увидеть дублирующиеся идентификаторы операций.

Чтобы обойти это, вы можете переопределить `get_operation_id_base()`, чтобы предоставить другую базу для именной части ID.

#### `get_serializer()`.

Если представление реализовало `get_serializer()`, возвращает результат.

#### `get_request_serializer()`.

По умолчанию возвращает `get_serializer()`, но может быть переопределен для различения объектов запроса и ответа.

#### `get_response_serializer()`.

По умолчанию возвращает `get_serializer()`, но может быть переопределен для различения объектов запроса и ответа.

### Именованные аргументы `AutoSchema.__init__()`

`AutoSchema` предоставляет ряд именованных аргументов `__init__()`, которые могут быть использованы для общей настройки, если сгенерированные по умолчанию значения не подходят.

Доступные именованные аргументы следующие:

* `tags`: Укажите список тегов.
* `component_name`: Укажите имя компонента.
* `operation_id_base`: Укажите часть имени ресурса в идентификаторах операций.

Вы передаете именованные аргументы при объявлении экземпляра `AutoSchema` в вашем представлении:

```python
class PetDetailView(generics.RetrieveUpdateDestroyAPIView):
    schema = AutoSchema(
        tags=['Pets'],
        component_name='Pet',
        operation_id_base='Pet',
    )
    ...
```

Предполагая модель `Pet` и сериализатор `PetSerializer`, именованные аргументы в этом примере, вероятно, не нужны. Однако, часто вам придется передавать именованные аргументы, если у вас есть несколько представлений, нацеленных на одну и ту же модель, или несколько представлений с одинаковыми именами сериализаторов.

Если ваши представления имеют связанные настройки, которые часто требуются, вы можете создать базовый подкласс `AutoSchema` для вашего проекта, который принимает дополнительные `__init__()` именованные аргументы, чтобы избежать подклассификации `AutoSchema` для каждого представления.


---

# 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/schemas.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.
