# Модель ответа — Возвращаемый тип { #response-model-return-type } Вы можете объявить тип, используемый для ответа, указав аннотацию **возвращаемого значения** для *функции-обработчика пути*. Вы можете использовать **аннотации типов** так же, как и для входных данных в **параметрах** функции: Pydantic-модели, списки, словари, скалярные значения (целые числа, булевы и т.д.). {* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *} FastAPI будет использовать этот возвращаемый тип, чтобы: * **Валидировать** возвращаемые данные. * Если данные невалидны (например, отсутствует поле), это означает, что код *вашего* приложения работает некорректно и возвращает не то, что должен. В таком случае будет возвращена ошибка сервера вместо неправильных данных. Так вы и ваши клиенты можете быть уверены, что получите ожидаемые данные и ожидаемую структуру данных. * Добавить **JSON Schema** для ответа в OpenAPI *операции пути*. * Это будет использовано **автоматической документацией**. * Это также будет использовано инструментами автоматической генерации клиентского кода. Но самое главное: * Выходные данные будут **ограничены и отфильтрованы** в соответствии с тем, что определено в возвращаемом типе. * Это особенно важно для **безопасности**, ниже мы рассмотрим это подробнее. ## Параметр `response_model` { #response-model-parameter } Бывают случаи, когда вам нужно или хочется возвращать данные, которые не в точности соответствуют объявленному типу. Например, вы можете хотеть **возвращать словарь** или объект из базы данных, но **объявить его как Pydantic-модель**. Тогда Pydantic-модель выполнит документирование данных, валидацию и т.п. для объекта, который вы вернули (например, словаря или объекта из базы данных). Если вы добавите аннотацию возвращаемого типа, инструменты и редакторы кода начнут жаловаться (и будут правы), что функция возвращает тип (например, dict), отличный от объявленного (например, Pydantic-модель). В таких случаях вместо аннотации возвращаемого типа можно использовать параметр `response_model` у *декоратора операции пути*. Параметр `response_model` можно указать у любой *операции пути*: * `@app.get()` * `@app.post()` * `@app.put()` * `@app.delete()` * и т.д. {* ../../docs_src/response_model/tutorial001_py310.py hl[17,22,24:27] *} /// note | Примечание Обратите внимание, что `response_model` — это параметр метода «декоратора» (`get`, `post` и т.д.), а не вашей *функции-обработчика пути*, в которой указываются параметры и тело запроса. /// `response_model` принимает тот же тип, что вы бы объявили для поля Pydantic-модели, то есть это может быть одна Pydantic-модель, а может быть, например, `list` Pydantic-моделей, как `List[Item]`. FastAPI будет использовать этот `response_model` для документирования, валидации данных и т.п., а также для **конвертации и фильтрации выходных данных** к объявленному типу. /// tip | Совет Если у вас в редакторе кода, mypy и т.п. включены строгие проверки типов, вы можете объявить возвращаемый тип функции как `Any`. Так вы сообщите редактору, что намеренно возвращаете что угодно. Но FastAPI всё равно выполнит документирование данных, валидацию, фильтрацию и т.д. с помощью `response_model`. /// ### Приоритет `response_model` { #response-model-priority } Если вы объявите и возвращаемый тип, и `response_model`, приоритет будет у `response_model`, именно его использует FastAPI. Так вы можете добавить корректные аннотации типов к своим функциям, даже если фактически возвращаете тип, отличный от модели ответа, чтобы ими пользовались редактор кода и инструменты вроде mypy. И при этом FastAPI продолжит выполнять валидацию данных, документацию и т.д. с использованием `response_model`. Вы также можете указать `response_model=None`, чтобы отключить создание модели ответа для данной *операции пути*. Это может понадобиться, если вы добавляете аннотации типов для вещей, не являющихся валидными полями Pydantic. Пример вы увидите ниже. ## Вернуть те же входные данные { #return-the-same-input-data } Здесь мы объявляем модель `UserIn`, она будет содержать пароль в открытом виде: {* ../../docs_src/response_model/tutorial002_py310.py hl[7,9] *} /// info | Информация Чтобы использовать `EmailStr`, сначала установите `email-validator`. Убедитесь, что вы создали [виртуальное окружение](../virtual-environments.md){.internal-link target=_blank}, активировали его, а затем установите пакет, например: ```console $ pip install email-validator ``` или так: ```console $ pip install "pydantic[email]" ``` /// И мы используем эту модель для объявления входных данных, и ту же модель — для объявления выходных данных: {* ../../docs_src/response_model/tutorial002_py310.py hl[16] *} Теперь, когда браузер создаёт пользователя с паролем, API вернёт тот же пароль в ответе. В этом случае это может быть не проблемой, так как пароль отправляет тот же пользователь. Но если мы используем ту же модель в другой *операции пути*, мы можем начать отправлять пароли пользователей каждому клиенту. /// danger | Осторожно Никогда не храните пароль пользователя в открытом виде и не отправляйте его в ответе подобным образом, если только вы не понимаете всех рисков и точно знаете, что делаете. /// ## Добавить выходную модель { #add-an-output-model } Вместо этого мы можем создать входную модель с паролем в открытом виде и выходную модель без него: {* ../../docs_src/response_model/tutorial003_py310.py hl[9,11,16] *} Здесь, хотя *функция-обработчик пути* возвращает тот же входной объект пользователя, содержащий пароль: {* ../../docs_src/response_model/tutorial003_py310.py hl[24] *} ...мы объявили `response_model` как модель `UserOut`, в которой нет пароля: {* ../../docs_src/response_model/tutorial003_py310.py hl[22] *} Таким образом, **FastAPI** позаботится о том, чтобы отфильтровать все данные, не объявленные в выходной модели (используя Pydantic). ### `response_model` или возвращаемый тип { #response-model-or-return-type } В этом случае, поскольку две модели различаются, если бы мы аннотировали возвращаемый тип функции как `UserOut`, редактор кода и инструменты пожаловались бы, что мы возвращаем неверный тип, так как это разные классы. Поэтому в этом примере мы должны объявить тип ответа в параметре `response_model`. ...но читайте дальше, чтобы узнать, как это обойти. ## Возвращаемый тип и фильтрация данных { #return-type-and-data-filtering } Продолжим предыдущий пример. Мы хотели **аннотировать функцию одним типом**, но при этом иметь возможность вернуть из функции что-то, что фактически включает **больше данных**. Мы хотим, чтобы FastAPI продолжал **фильтровать** данные с помощью модели ответа. Так что, даже если функция возвращает больше данных, в ответ будут включены только поля, объявленные в модели ответа. В предыдущем примере, поскольку классы были разными, нам пришлось использовать параметр `response_model`. Но это также означает, что мы теряем поддержку от редактора кода и инструментов, проверяющих возвращаемый тип функции. Однако в большинстве таких случаев нам нужно лишь **отфильтровать/убрать** некоторые данные, как в этом примере. И в этих случаях мы можем использовать классы и наследование, чтобы воспользоваться **аннотациями типов** функций для лучшей поддержки в редакторе кода и инструментах и при этом получить **фильтрацию данных** от FastAPI. {* ../../docs_src/response_model/tutorial003_01_py310.py hl[7:10,13:14,18] *} Так мы получаем поддержку инструментов — редакторов кода и mypy, так как этот код корректен с точки зрения типов — и одновременно получаем фильтрацию данных от FastAPI. Как это работает? Давайте разберёмся. 🤓 ### Аннотации типов и инструменты { #type-annotations-and-tooling } Сначала посмотрим, как это увидят редактор кода, mypy и другие инструменты. `BaseUser` содержит базовые поля. Затем `UserIn` наследуется от `BaseUser` и добавляет поле `password`, то есть он будет включать все поля обеих моделей. Мы аннотируем возвращаемый тип функции как `BaseUser`, но фактически возвращаем экземпляр `UserIn`. Редактор кода, mypy и другие инструменты не будут возражать, потому что с точки зрения типов `UserIn` — подкласс `BaseUser`, что означает, что это *валидный* тип везде, где ожидается что-то, являющееся `BaseUser`. ### Фильтрация данных FastAPI { #fastapi-data-filtering } Теперь для FastAPI: он увидит возвращаемый тип и убедится, что то, что вы возвращаете, включает **только** поля, объявленные в этом типе. FastAPI делает несколько вещей внутри вместе с Pydantic, чтобы гарантировать, что те же правила наследования классов не используются для фильтрации возвращаемых данных, иначе вы могли бы в итоге вернуть намного больше данных, чем ожидали. Таким образом вы получаете лучшее из обоих миров: аннотации типов с **поддержкой инструментов** и **фильтрацию данных**. ## Посмотреть в документации { #see-it-in-the-docs } В автоматической документации вы увидите, что у входной и выходной моделей есть свои JSON Schema: И обе модели будут использоваться в интерактивной документации API: ## Другие аннотации возвращаемых типов { #other-return-type-annotations } Бывают случаи, когда вы возвращаете что-то, что не является валидным полем Pydantic, и аннотируете это в функции только ради поддержки инструментов (редактор кода, mypy и т.д.). ### Возврат Response напрямую { #return-a-response-directly } Самый распространённый случай — [возвращать Response напрямую, как описано далее в разделах документации для продвинутых](../advanced/response-directly.md){.internal-link target=_blank}. {* ../../docs_src/response_model/tutorial003_02_py39.py hl[8,10:11] *} Этот простой случай обрабатывается FastAPI автоматически, потому что аннотация возвращаемого типа — это класс (или подкласс) `Response`. И инструменты тоже будут довольны, потому что и `RedirectResponse`, и `JSONResponse` являются подклассами `Response`, так что аннотация типа корректна. ### Аннотировать подкласс Response { #annotate-a-response-subclass } Вы также можете использовать подкласс `Response` в аннотации типа: {* ../../docs_src/response_model/tutorial003_03_py39.py hl[8:9] *} Это тоже сработает, так как `RedirectResponse` — подкласс `Response`, и FastAPI автоматически обработает этот простой случай. ### Некорректные аннотации возвращаемых типов { #invalid-return-type-annotations } Но когда вы возвращаете произвольный объект, не являющийся валидным типом Pydantic (например, объект базы данных), и аннотируете его таким образом в функции, FastAPI попытается создать модель ответа Pydantic из этой аннотации типа и потерпит неудачу. То же произойдёт, если у вас будет что-то вроде union разных типов, где один или несколько не являются валидными типами Pydantic, например, это приведёт к ошибке 💥: {* ../../docs_src/response_model/tutorial003_04_py310.py hl[8] *} ...это не сработает, потому что аннотация типа не является типом Pydantic и это не единственный класс `Response` или его подкласс, а объединение (`union`) из `Response` и `dict`. ### Отключить модель ответа { #disable-response-model } Продолжая пример выше, вы можете не хотеть использовать стандартные валидацию данных, документирование, фильтрацию и т.п., выполняемые FastAPI. Но при этом вы можете хотеть сохранить аннотацию возвращаемого типа в функции, чтобы пользоваться поддержкой инструментов вроде редакторов кода и инструментов проверки типов (например, mypy). В этом случае вы можете отключить генерацию модели ответа, установив `response_model=None`: {* ../../docs_src/response_model/tutorial003_05_py310.py hl[7] *} Так FastAPI пропустит генерацию модели ответа, и вы сможете использовать любые аннотации возвращаемых типов, которые вам нужны, без влияния на ваше приложение FastAPI. 🤓 ## Параметры кодирования модели ответа { #response-model-encoding-parameters } У вашей модели ответа могут быть значения по умолчанию, например: {* ../../docs_src/response_model/tutorial004_py310.py hl[9,11:12] *} * `description: Union[str, None] = None` (или `str | None = None` в Python 3.10) имеет значение по умолчанию `None`. * `tax: float = 10.5` имеет значение по умолчанию `10.5`. * `tags: List[str] = []` имеет значение по умолчанию пустого списка: `[]`. но вы можете захотеть опустить их в результате, если они фактически не были сохранены. Например, если у вас есть модели с множеством необязательных атрибутов в NoSQL-базе данных, но вы не хотите отправлять очень длинные JSON-ответы, заполненные значениями по умолчанию. ### Используйте параметр `response_model_exclude_unset` { #use-the-response-model-exclude-unset-parameter } Вы можете установить у *декоратора операции пути* параметр `response_model_exclude_unset=True`: {* ../../docs_src/response_model/tutorial004_py310.py hl[22] *} и эти значения по умолчанию не будут включены в ответ — только те значения, которые действительно были установлены. Итак, если вы отправите запрос к этой *операции пути* для элемента с ID `foo`, ответ (без значений по умолчанию) будет таким: ```JSON { "name": "Foo", "price": 50.2 } ``` /// info | Информация Вы также можете использовать: * `response_model_exclude_defaults=True` * `response_model_exclude_none=True` как описано в документации Pydantic для `exclude_defaults` и `exclude_none`. /// #### Данные со значениями для полей, имеющих значения по умолчанию { #data-with-values-for-fields-with-defaults } Но если в ваших данных есть значения для полей модели, для которых указаны значения по умолчанию, как у элемента с ID `bar`: ```Python hl_lines="3 5" { "name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2 } ``` они будут включены в ответ. #### Данные с такими же значениями, как значения по умолчанию { #data-with-the-same-values-as-the-defaults } Если данные имеют те же значения, что и значения по умолчанию, как у элемента с ID `baz`: ```Python hl_lines="3 5-6" { "name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": [] } ``` FastAPI достаточно умен (на самом деле, это Pydantic), чтобы понять, что хотя `description`, `tax` и `tags` совпадают со значениями по умолчанию, они были установлены явно (а не взяты из значений по умолчанию). Поэтому они тоже будут включены в JSON-ответ. /// tip | Совет Обратите внимание, что значения по умолчанию могут быть любыми, не только `None`. Это может быть список (`[]`), число с плавающей точкой `10.5` и т.д. /// ### `response_model_include` и `response_model_exclude` { #response-model-include-and-response-model-exclude } Вы также можете использовать параметры *декоратора операции пути* `response_model_include` и `response_model_exclude`. Они принимают `set` из `str` с именами атрибутов, которые нужно включить (исключив остальные) или исключить (оставив остальные). Это можно использовать как быстрый способ, если у вас только одна Pydantic-модель и вы хотите убрать часть данных из ответа. /// tip | Совет Но всё же рекомендуется использовать подходы выше — несколько классов — вместо этих параметров. Потому что JSON Schema, генерируемая в OpenAPI вашего приложения (и документации), всё равно будет соответствовать полной модели, даже если вы используете `response_model_include` или `response_model_exclude`, чтобы опустить некоторые атрибуты. То же относится к `response_model_by_alias`, который работает аналогично. /// {* ../../docs_src/response_model/tutorial005_py310.py hl[29,35] *} /// tip | Совет Синтаксис `{"name", "description"}` создаёт `set` с этими двумя значениями. Это эквивалентно `set(["name", "description"])`. /// #### Использование `list` вместо `set` { #using-lists-instead-of-sets } Если вы забыли использовать `set` и применили `list` или `tuple` вместо него, FastAPI всё равно преобразует это в `set`, и всё будет работать корректно: {* ../../docs_src/response_model/tutorial006_py310.py hl[29,35] *} ## Резюме { #recap } Используйте параметр `response_model` у *декоратора операции пути*, чтобы задавать модели ответа, и особенно — чтобы приватные данные отфильтровывались. Используйте `response_model_exclude_unset`, чтобы возвращать только те значения, которые были установлены явно.