# Query-параметры и валидация строк { #query-parameters-and-string-validations } **FastAPI** позволяет определять дополнительную информацию и выполнять валидацию для ваших параметров. Рассмотрим это приложение в качестве примера: {* ../../docs_src/query_params_str_validations/tutorial001_py310.py hl[7] *} Query-параметр `q` имеет тип `str | None`, это означает, что он имеет тип `str`, но также может быть `None`. Значение по умолчанию действительно `None`, поэтому FastAPI будет знать, что он не обязателен. /// note | Примечание FastAPI поймёт, что значение `q` не обязательно, из‑за значения по умолчанию `= None`. Аннотация `str | None` позволит вашему редактору кода обеспечить лучшую поддержку и находить ошибки. /// ## Дополнительная валидация { #additional-validation } Мы собираемся добавить ограничение: хотя `q` и необязателен, когда он передан, **его длина не должна превышать 50 символов**. ### Импорт `Query` и `Annotated` { #import-query-and-annotated } Чтобы сделать это, сначала импортируйте: * `Query` из `fastapi` * `Annotated` из `typing` {* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[1,3] *} /// info | Дополнительная информация Поддержка `Annotated` (и рекомендация использовать его) появилась в FastAPI версии 0.95.0. Если у вас более старая версия, при попытке использовать `Annotated` вы получите ошибки. Убедитесь, что вы [обновили версию FastAPI](../deployment/versions.md#upgrading-the-fastapi-versions) как минимум до 0.95.1 перед использованием `Annotated`. /// ## Использовать `Annotated` в типе для параметра `q` { #use-annotated-in-the-type-for-the-q-parameter } Помните, я уже говорил, что `Annotated` можно использовать для добавления метаданных к параметрам в разделе [Введение в типы Python](../python-types.md#type-hints-with-metadata-annotations)? Пришло время использовать его с FastAPI. 🚀 У нас была такая аннотация типа: ```Python q: str | None = None ``` Мы «обернём» это в `Annotated`, и получится: ```Python q: Annotated[str | None] = None ``` Обе версии означают одно и то же: `q` — параметр, который может быть `str` или `None`, и по умолчанию равен `None`. А теперь к самому интересному. 🎉 ## Добавим `Query` в `Annotated` для параметра `q` { #add-query-to-annotated-in-the-q-parameter } Теперь, когда у нас есть `Annotated`, куда можно поместить дополнительную информацию (в нашем случае — дополнительные правила валидации), добавим `Query` внутрь `Annotated` и установим параметр `max_length` равным `50`: {* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[9] *} Обратите внимание, что значение по умолчанию по‑прежнему `None`, то есть параметр остаётся необязательным. Но теперь, добавив `Query(max_length=50)` внутрь `Annotated`, мы говорим FastAPI, что этому значению нужна **дополнительная валидация** — максимум 50 символов. 😎 /// tip | Совет Здесь мы используем `Query()`, потому что это **query-параметр**. Позже мы увидим другие — `Path()`, `Body()`, `Header()` и `Cookie()`, — они также принимают те же аргументы, что и `Query()`. /// Теперь FastAPI будет: * **валидировать** данные, удостоверяясь, что максимальная длина — 50 символов; * показывать **понятную ошибку** клиенту, если данные невалидны; * **документировать** параметр в *операции пути* схемы OpenAPI (он будет показан в **UI автоматической документации**). ## Альтернатива (устаревшее): `Query` как значение по умолчанию { #alternative-old-query-as-the-default-value } В предыдущих версиях FastAPI (до 0.95.0) требовалось использовать `Query` как значение по умолчанию для параметра вместо помещения его в `Annotated`. Скорее всего вы ещё встретите такой код, поэтому поясню. /// tip | Подсказка Для нового кода и везде, где это возможно, используйте `Annotated`, как описано выше. У этого есть несколько преимуществ (см. ниже) и нет недостатков. 🍰 /// Вот как можно использовать `Query()` как значение по умолчанию для параметра функции, установив `max_length` равным 50: {* ../../docs_src/query_params_str_validations/tutorial002_py310.py hl[7] *} Так как в этом случае (без `Annotated`) мы заменяем в функции значение по умолчанию `None` на `Query()`, теперь нужно указать значение по умолчанию через параметр `Query(default=None)`, это служит той же цели — задать значение по умолчанию (по крайней мере для FastAPI). Итак: ```Python q: str | None = Query(default=None) ``` ...делает параметр необязательным со значением по умолчанию `None`, так же как: ```Python q: str | None = None ``` Но вариант с `Query` явно объявляет его как query-параметр. Затем мы можем передать и другие параметры в `Query`. В данном случае — параметр `max_length`, применимый к строкам: ```Python q: str | None = Query(default=None, max_length=50) ``` Это провалидирует данные, покажет понятную ошибку, если данные невалидны, и задокументирует параметр в *операции пути* схемы OpenAPI. ### `Query` как значение по умолчанию или внутри `Annotated` { #query-as-the-default-value-or-in-annotated } Помните, что при использовании `Query` внутри `Annotated` нельзя указывать параметр `default` у `Query`. Вместо этого используйте обычное значение по умолчанию параметра функции. Иначе это будет неоднозначно. Например, так делать нельзя: ```Python q: Annotated[str, Query(default="rick")] = "morty" ``` ...потому что непонятно, какое значение должно быть по умолчанию: `"rick"` или `"morty"`. Следовательно, используйте (предпочтительно): ```Python q: Annotated[str, Query()] = "rick" ``` ...или в старой кодовой базе вы увидите: ```Python q: str = Query(default="rick") ``` ### Преимущества `Annotated` { #advantages-of-annotated } **Рекомендуется использовать `Annotated`** вместо задания значения по умолчанию в параметрах функции — так **лучше** по нескольким причинам. 🤓 **Значение по умолчанию** у **параметра функции** — это **настоящее значение по умолчанию**, что более интуитивно для Python. 😌 Вы можете **вызвать** эту же функцию в **других местах** без FastAPI, и она будет **работать как ожидается**. Если есть **обязательный** параметр (без значения по умолчанию), ваш **редактор** сообщит об ошибке, **Python** тоже пожалуется, если вы запустите её без передачи обязательного параметра. Если вы не используете `Annotated`, а применяете **(устаревший) стиль со значением по умолчанию**, то при вызове этой функции без FastAPI в **других местах** вам нужно **помнить** о том, что надо передать аргументы, чтобы всё работало корректно, иначе значения будут не такими, как вы ожидаете (например, вместо `str` будет `QueryInfo` или что-то подобное). И ни редактор, ни Python не будут ругаться при самом вызове функции — ошибка проявится лишь при операциях внутри. Так как `Annotated` может содержать больше одной аннотации метаданных, теперь вы можете использовать ту же функцию и с другими инструментами, например с [Typer](https://typer.tiangolo.com/). 🚀 ## Больше валидаций { #add-more-validations } Можно также добавить параметр `min_length`: {* ../../docs_src/query_params_str_validations/tutorial003_an_py310.py hl[10] *} ## Регулярные выражения { #add-regular-expressions } Вы можете определить регулярное выражение `pattern`, которому должен соответствовать параметр: {* ../../docs_src/query_params_str_validations/tutorial004_an_py310.py hl[11] *} Данный шаблон регулярного выражения проверяет, что полученное значение параметра: * `^`: начинается с следующих символов, до них нет символов. * `fixedquery`: имеет точное значение `fixedquery`. * `$`: заканчивается здесь, после `fixedquery` нет никаких символов. Если вы теряетесь во всех этих идеях про **«регулярные выражения»**, не переживайте. Это сложная тема для многих. Многое можно сделать и без них. Теперь вы знаете, что когда они понадобятся, вы сможете использовать их в **FastAPI**. ## Значения по умолчанию { #default-values } Конечно, можно использовать и другие значения по умолчанию, не только `None`. Допустим, вы хотите объявить, что query-параметр `q` должен иметь `min_length` равный `3` и значение по умолчанию `"fixedquery"`: {* ../../docs_src/query_params_str_validations/tutorial005_an_py310.py hl[9] *} /// note | Примечание Наличие значения по умолчанию любого типа, включая `None`, делает параметр необязательным. /// ## Обязательные параметры { #required-parameters } Когда не требуется объявлять дополнительные проверки или метаданные, можно сделать query-параметр `q` обязательным, просто не указывая значение по умолчанию, например: ```Python q: str ``` вместо: ```Python q: str | None = None ``` Но сейчас мы объявляем его через `Query`, например так: ```Python q: Annotated[str | None, Query(min_length=3)] = None ``` Поэтому, когда вам нужно объявить значение как обязательное при использовании `Query`, просто не указывайте значение по умолчанию: {* ../../docs_src/query_params_str_validations/tutorial006_an_py310.py hl[9] *} ### Обязательный, но может быть `None` { #required-can-be-none } Можно объявить, что параметр может принимать `None`, но при этом остаётся обязательным. Это заставит клиентов отправлять значение, даже если это значение — `None`. Для этого объявите, что `None` — валидный тип, но просто не задавайте значение по умолчанию: {* ../../docs_src/query_params_str_validations/tutorial006c_an_py310.py hl[9] *} ## Query-параметр - список / несколько значений { #query-parameter-list-multiple-values } Когда вы явно объявляете query-параметр через `Query`, можно также указать, что он принимает список значений, иначе говоря — несколько значений. Например, чтобы объявить query-параметр `q`, который может встречаться в URL несколько раз, можно написать: {* ../../docs_src/query_params_str_validations/tutorial011_an_py310.py hl[9] *} Тогда при таком URL: ``` http://localhost:8000/items/?q=foo&q=bar ``` вы получите множественные значения *query-параметров* `q` (`foo` и `bar`) в виде Python-`list` внутри вашей *функции-обработчика пути*, в *параметре функции* `q`. Таким образом, ответ на этот URL будет: ```JSON { "q": [ "foo", "bar" ] } ``` /// tip | Совет Чтобы объявить query-параметр типа `list`, как в примере выше, нужно явно использовать `Query`, иначе он будет интерпретирован как тело запроса. /// Интерактивная документация API обновится соответствующим образом и позволит передавать несколько значений: ### Query-параметр - список / несколько значений со значением по умолчанию { #query-parameter-list-multiple-values-with-defaults } Можно также определить значение по умолчанию как `list`, если ничего не передано: {* ../../docs_src/query_params_str_validations/tutorial012_an_py310.py hl[9] *} Если вы перейдёте по адресу: ``` http://localhost:8000/items/ ``` значение по умолчанию для `q` будет: `["foo", "bar"]`, и ответом будет: ```JSON { "q": [ "foo", "bar" ] } ``` #### Просто `list` { #using-just-list } Можно использовать `list` напрямую вместо `list[str]`: {* ../../docs_src/query_params_str_validations/tutorial013_an_py310.py hl[9] *} /// note | Примечание Имейте в виду, что в этом случае FastAPI не будет проверять содержимое списка. Например, `list[int]` проверит (и задокументирует), что элементы списка — целые числа. А просто `list` — нет. /// ## Больше метаданных { #declare-more-metadata } Можно добавить больше информации о параметре. Эта информация будет включена в сгенерированную OpenAPI-схему и использована интерфейсами документации и внешними инструментами. /// note | Примечание Помните, что разные инструменты могут иметь разный уровень поддержки OpenAPI. Некоторые из них пока могут не показывать всю дополнительную информацию, хотя в большинстве случаев недостающая возможность уже запланирована к разработке. /// Можно задать `title`: {* ../../docs_src/query_params_str_validations/tutorial007_an_py310.py hl[10] *} И `description`: {* ../../docs_src/query_params_str_validations/tutorial008_an_py310.py hl[14] *} ## Псевдонимы параметров { #alias-parameters } Представьте, что вы хотите, чтобы параметр назывался `item-query`. Например: ``` http://127.0.0.1:8000/items/?item-query=foobaritems ``` Но `item-query` — недопустимое имя переменной в Python. Ближайший вариант — `item_query`. Но вам всё равно нужно именно `item-query`... Тогда можно объявить `alias`, и этот псевдоним будет использован для поиска значения параметра: {* ../../docs_src/query_params_str_validations/tutorial009_an_py310.py hl[9] *} ## Маркировка параметров как устаревших { #deprecating-parameters } Предположим, этот параметр вам больше не нравится. Его нужно оставить на какое‑то время, так как клиенты его используют, но вы хотите, чтобы в документации он явно отображался как устаревший. Тогда передайте параметр `deprecated=True` в `Query`: {* ../../docs_src/query_params_str_validations/tutorial010_an_py310.py hl[19] *} В документации это будет показано так: ## Исключить параметры из OpenAPI { #exclude-parameters-from-openapi } Чтобы исключить query-параметр из генерируемой OpenAPI-схемы (и, следовательно, из систем автоматической документации), укажите у `Query` параметр `include_in_schema=False`: {* ../../docs_src/query_params_str_validations/tutorial014_an_py310.py hl[10] *} ## Кастомная валидация { #custom-validation } Бывают случаи, когда нужна **кастомная валидация**, которую нельзя выразить параметрами выше. В таких случаях можно использовать **кастомную функцию-валидатор**, которая применяется после обычной валидации (например, после проверки, что значение — это `str`). Этого можно добиться, используя [`AfterValidator` Pydantic](https://docs.pydantic.dev/latest/concepts/validators/#field-after-validator) внутри `Annotated`. /// tip | Совет В Pydantic также есть [`BeforeValidator`](https://docs.pydantic.dev/latest/concepts/validators/#field-before-validator) и другие. 🤓 /// Например, эта кастомная проверка убеждается, что ID элемента начинается с `isbn-` для номера книги ISBN или с `imdb-` для ID URL фильма на IMDB: {* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *} /// info | Дополнительная информация Это доступно в Pydantic версии 2 и выше. 😎 /// /// tip | Совет Если вам нужна валидация, требующая общения с каким‑либо **внешним компонентом** — базой данных или другим API — вместо этого используйте **Зависимости FastAPI**, вы познакомитесь с ними позже. Эти кастомные валидаторы предназначены для проверок, которые можно выполнить, имея **только** те же **данные**, что пришли в запросе. /// ### Понимание этого кода { #understand-that-code } Важный момент — это использовать **`AfterValidator` с функцией внутри `Annotated`**. Смело пропускайте эту часть. 🤸 --- Но если вам любопытен именно этот пример и всё ещё интересно, вот немного подробностей. #### Строка и `value.startswith()` { #string-with-value-startswith } Заметили? Метод строки `value.startswith()` может принимать кортеж — тогда будет проверено каждое значение из кортежа: {* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[16:19] hl[17] *} #### Случайный элемент { #a-random-item } С помощью `data.items()` мы получаем итерируемый объект с кортежами, содержащими ключ и значение для каждого элемента словаря. Мы превращаем этот итерируемый объект в обычный `list` через `list(data.items())`. Затем с `random.choice()` можно получить **случайное значение** из списка — то есть кортеж вида `(id, name)`. Это будет что‑то вроде `("imdb-tt0371724", "The Hitchhiker's Guide to the Galaxy")`. После этого мы **присваиваем эти два значения** кортежа переменным `id` и `name`. Так что, если пользователь не передал ID элемента, он всё равно получит случайную рекомендацию. ...и всё это в **одной простой строке**. 🤯 Разве не прекрасен Python? 🐍 {* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[22:30] hl[29] *} ## Резюме { #recap } Вы можете объявлять дополнительные проверки и метаданные для параметров. Общие метаданные и настройки: * `alias` * `title` * `description` * `deprecated` Проверки, специфичные для строк: * `min_length` * `max_length` * `pattern` Кастомные проверки с использованием `AfterValidator`. В этих примерах вы видели, как объявлять проверки для значений типа `str`. Смотрите следующие главы, чтобы узнать, как объявлять проверки для других типов, например чисел.