Query-параметр q имеет тип Union[str, None] (или str | None в Python 3.10). Это означает, что входной параметр будет типа str, но может быть и None. Ещё параметр имеет значение по умолчанию None, из-за чего FastAPI определит параметр как необязательный.
Технические детали
FastAPI определит параметр q как необязательный, потому что его значение по умолчанию = None.
Union в Union[str, None] позволит редактору кода оказать вам лучшую поддержку и найти ошибки.
Добавим дополнительное условие валидации параметра q - длина строки не более 50 символов (условие проверяется всякий раз, когда параметр q не является None).
Помните, как ранее я говорил об Annotated? Он может быть использован для добавления метаданных для ваших параметров в разделе Введение в аннотации типов Python?
Пришло время использовать их в FastAPI. 🚀
У нас была аннотация следующего типа:
q:str|None=None
q:Union[str,None]=None
Вот что мы получим, если обернём это в Annotated:
q:Annotated[str|None]=None
q:Annotated[Union[str,None]]=None
Обе эти версии означают одно и тоже. q - это параметр, который может быть str или None, и по умолчанию он будет принимать None.
Обратите внимание, что значение по умолчанию всё ещё None, так что параметр остаётся необязательным.
Однако теперь, имея Query(max_length=50) внутри Annotated, мы говорим FastAPI, что мы хотим извлечь это значение из параметров query-запроса (что произойдёт в любом случае 🤷), и что мы хотим иметь дополнительные условия валидации для этого значения (для чего мы и делаем это - чтобы получить дополнительную валидацию). 😎
Теперь FastAPI:
Валидирует (проверяет), что полученные данные состоят максимум из 50 символов
Показывает исчерпывающую ошибку (будет описание местонахождения ошибки и её причины) для клиента в случаях, когда данные не валидны
Задокументирует параметр в схему OpenAPI операции пути (что будет отображено в UI автоматической документации)
Альтернативный (устаревший) способ задать Query как значение по умолчанию¶
В предыдущих версиях FastAPI (ниже 0.95.0) необходимо было использовать Query как значение по умолчанию для query-параметра. Так было вместо размещения его в Annotated, так что велика вероятность, что вам встретится такой код. Сейчас объясню.
Подсказка
При написании нового кода и везде где это возможно, используйте Annotated, как было описано ранее. У этого способа есть несколько преимуществ (о них дальше) и никаких недостатков. 🍰
Вот как вы могли бы использовать Query() в качестве значения по умолчанию параметра вашей функции, установив для параметра max_length значение 50:
В таком случае (без использования Annotated), мы заменили значение по умолчанию с None на Query() в функции. Теперь нам нужно установить значение по умолчанию для query-параметра Query(default=None), что необходимо для тех же целей, как когда ранее просто указывалось значение по умолчанию (по крайней мере, для FastAPI).
Таким образом:
q:Union[str,None]=Query(default=None)
...делает параметр необязательным со значением по умолчанию None, также как это делает:
q:Union[str,None]=None
И для Python 3.10 и выше:
q:str|None=Query(default=None)
...делает параметр необязательным со значением по умолчанию None, также как это делает:
q:str|None=None
Но он явно объявляет его как query-параметр.
Дополнительная информация
Запомните, важной частью объявления параметра как необязательного является:
=None
или:
=Query(default=None)
так как None указан в качестве значения по умолчанию, параметр будет необязательным.
Union[str, None] позволит редактору кода оказать вам лучшую поддержку. Но это не то, на что обращает внимание FastAPI для определения необязательности параметра.
Теперь, мы можем указать больше параметров для Query. В данном случае, параметр max_length применяется к строкам:
Входные данные будут проверены. Если данные недействительны, тогда будет указано на ошибку в запросе (будет описание местонахождения ошибки и её причины). Кроме того, параметр задокументируется в схеме OpenAPI данной операции пути.
Использовать Query как значение по умолчанию или добавить в Annotated¶
Когда Query используется внутри Annotated, вы не можете использовать параметр default у Query.
Вместо этого, используйте обычное указание значения по умолчанию для параметра функции. Иначе, это будет несовместимо.
Следующий пример не рабочий:
q:Annotated[str,Query(default="rick")]="morty"
...потому что нельзя однозначно определить, что именно должно быть значением по умолчанию: "rick" или "morty".
Вам следует использовать (предпочтительно):
q:Annotated[str,Query()]="rick"
...или как в старом коде, который вам может попасться:
Рекомендуется использовать Annotated вместо значения по умолчанию в параметрах функции, потому что так лучше по нескольким причинам. 🤓
Значение по умолчанию у параметров функции - это действительно значение по умолчанию, что более интуитивно понятно для пользователей Python. 😌
Вы можете вызвать ту же функцию в иных местах без FastAPI, и она сработает как ожидается. Если это обязательный параметр (без значения по умолчанию), ваш редактор кода сообщит об ошибке. Python также укажет на ошибку, если вы вызовете функцию без передачи ей обязательного параметра.
Если вы вместо Annotated используете (устаревший) стиль значений по умолчанию, тогда при вызове этой функции без FastAPI в другом месте вам необходимо помнить о передаче аргументов функции, чтобы она работала корректно. В противном случае, значения будут отличаться от тех, что вы ожидаете (например, QueryInfo или что-то подобное вместо str). И ни ваш редактор кода, ни Python не будут жаловаться на работу этой функции, только когда вычисления внутри дадут сбой.
Так как Annotated может принимать более одной аннотации метаданных, то теперь вы можете использовать ту же функцию с другими инструментами, например Typer. 🚀
Данное регулярное выражение проверяет, что полученное значение параметра:
^: начало строки.
fixedquery: в точности содержит строку fixedquery.
$: конец строки, не имеет символов после fixedquery.
Не переживайте, если "регулярное выражение" вызывает у вас трудности. Это достаточно сложная тема для многих людей. Вы можете сделать множество вещей без использования регулярных выражений.
Но когда они вам понадобятся, и вы закончите их освоение, то не будет проблемой использовать их в FastAPI.
Вы точно также можете указать любое значение по умолчанию, как ранее указывали None.
Например, вы хотите для параметра запроса q указать, что он должен состоять минимум из 3 символов (min_length=3) и иметь значение по умолчанию "fixedquery":
Когда вам не требуется дополнительная валидация или дополнительные метаданные для параметра запроса, вы можете сделать параметр q обязательным просто не указывая значения по умолчанию. Например:
q:str
вместо:
q:Union[str,None]=None
Но у нас query-параметр определён как Query. Например:
Вы можете определить, что параметр может принимать None, но всё ещё является обязательным. Это может потребоваться для того, чтобы пользователи явно указали параметр, даже если его значение будет None.
Чтобы этого добиться, вам нужно определить None как валидный тип для параметра запроса, но также указать default=...:
Pydantic, мощь которого используется в FastAPI для валидации и сериализации, имеет специальное поведение для Optional или Union[Something, None] без значения по умолчанию. Вы можете узнать об этом больше в документации Pydantic, раздел Обязательные Опциональные поля.
Использование Pydantic's Required вместо Ellipsis (...)¶
Если вас смущает ..., вы можете использовать Required из Pydantic:
Запомните, когда вам необходимо объявить query-параметр обязательным, вы можете просто не указывать параметр default. Таким образом, вам редко придётся использовать ... или Required.
вы бы получили несколько значений (foo и bar), которые относятся к параметру q, в виде Python list внутри вашей функции обработки пути, в параметре функцииq.
Таким образом, ответ на этот URL будет:
{"q":["foo","bar"]}
Подсказка
Чтобы объявить query-параметр типом list, как в примере выше, вам нужно явно использовать Query, иначе он будет интерпретирован как тело запроса.
Интерактивная документация API будет обновлена соответствующим образом, где будет разрешено множество значений:
Query-параметр со множеством значений по умолчанию¶
Вы также можете указать тип list со списком значений по умолчанию на случай, если вам их не предоставят:
Запомните, что в таком случае, FastAPI не будет проверять содержимое списка.
Например, для List[int] список будет провалидирован (и задокументирован) на содержание только целочисленных элементов. Но для простого list такой проверки не будет.
Вы можете добавить больше информации об query-параметре.
Указанная информация будет включена в генерируемую OpenAPI документацию и использована в пользовательском интерфейсе и внешних инструментах.
Технические детали
Имейте в виду, что разные инструменты могут иметь разные уровни поддержки OpenAPI.
Некоторые из них могут не отображать (на данный момент) всю заявленную дополнительную информацию, хотя в большинстве случаев отсутствующая функция уже запланирована к разработке.
Вы можете указать название query-параметра, используя параметр title:
Добавить описание, используя параметр description:
fromtypingimportAnnotatedfromfastapiimportFastAPI,Queryapp=FastAPI()@app.get("/items/")asyncdefread_items(q:Annotated[str|None,Query(title="Query string",description="Query string for the items to search in the database that have a good match",min_length=3,),]=None):results={"items":[{"item_id":"Foo"},{"item_id":"Bar"}]}ifq:results.update({"q":q})returnresults
fromtypingimportAnnotated,UnionfromfastapiimportFastAPI,Queryapp=FastAPI()@app.get("/items/")asyncdefread_items(q:Annotated[Union[str,None],Query(title="Query string",description="Query string for the items to search in the database that have a good match",min_length=3,),]=None):results={"items":[{"item_id":"Foo"},{"item_id":"Bar"}]}ifq:results.update({"q":q})returnresults
fromtypingimportUnionfromfastapiimportFastAPI,Queryfromtyping_extensionsimportAnnotatedapp=FastAPI()@app.get("/items/")asyncdefread_items(q:Annotated[Union[str,None],Query(title="Query string",description="Query string for the items to search in the database that have a good match",min_length=3,),]=None):results={"items":[{"item_id":"Foo"},{"item_id":"Bar"}]}ifq:results.update({"q":q})returnresults
Подсказка
Рекомендуется использовать версию с Annotated если возможно.
fromfastapiimportFastAPI,Queryapp=FastAPI()@app.get("/items/")asyncdefread_items(q:str|None=Query(default=None,title="Query string",description="Query string for the items to search in the database that have a good match",min_length=3,)):results={"items":[{"item_id":"Foo"},{"item_id":"Bar"}]}ifq:results.update({"q":q})returnresults
Подсказка
Рекомендуется использовать версию с Annotated если возможно.
fromtypingimportUnionfromfastapiimportFastAPI,Queryapp=FastAPI()@app.get("/items/")asyncdefread_items(q:Union[str,None]=Query(default=None,title="Query string",description="Query string for the items to search in the database that have a good match",min_length=3,)):results={"items":[{"item_id":"Foo"},{"item_id":"Bar"}]}ifq:results.update({"q":q})returnresults
Предположим, вы больше не хотите использовать какой-либо параметр.
Вы решили оставить его, потому что клиенты всё ещё им пользуются. Но вы хотите отобразить это в документации как устаревший функционал.
Тогда для Query укажите параметр deprecated=True:
fromtypingimportAnnotatedfromfastapiimportFastAPI,Queryapp=FastAPI()@app.get("/items/")asyncdefread_items(q:Annotated[str|None,Query(alias="item-query",title="Query string",description="Query string for the items to search in the database that have a good match",min_length=3,max_length=50,pattern="^fixedquery$",deprecated=True,),]=None):results={"items":[{"item_id":"Foo"},{"item_id":"Bar"}]}ifq:results.update({"q":q})returnresults
fromtypingimportAnnotated,UnionfromfastapiimportFastAPI,Queryapp=FastAPI()@app.get("/items/")asyncdefread_items(q:Annotated[Union[str,None],Query(alias="item-query",title="Query string",description="Query string for the items to search in the database that have a good match",min_length=3,max_length=50,pattern="^fixedquery$",deprecated=True,),]=None):results={"items":[{"item_id":"Foo"},{"item_id":"Bar"}]}ifq:results.update({"q":q})returnresults
fromtypingimportUnionfromfastapiimportFastAPI,Queryfromtyping_extensionsimportAnnotatedapp=FastAPI()@app.get("/items/")asyncdefread_items(q:Annotated[Union[str,None],Query(alias="item-query",title="Query string",description="Query string for the items to search in the database that have a good match",min_length=3,max_length=50,pattern="^fixedquery$",deprecated=True,),]=None):results={"items":[{"item_id":"Foo"},{"item_id":"Bar"}]}ifq:results.update({"q":q})returnresults
Подсказка
Рекомендуется использовать версию с Annotated если возможно.
fromfastapiimportFastAPI,Queryapp=FastAPI()@app.get("/items/")asyncdefread_items(q:str|None=Query(default=None,alias="item-query",title="Query string",description="Query string for the items to search in the database that have a good match",min_length=3,max_length=50,pattern="^fixedquery$",deprecated=True,)):results={"items":[{"item_id":"Foo"},{"item_id":"Bar"}]}ifq:results.update({"q":q})returnresults
Подсказка
Рекомендуется использовать версию с Annotated если возможно.
fromtypingimportUnionfromfastapiimportFastAPI,Queryapp=FastAPI()@app.get("/items/")asyncdefread_items(q:Union[str,None]=Query(default=None,alias="item-query",title="Query string",description="Query string for the items to search in the database that have a good match",min_length=3,max_length=50,pattern="^fixedquery$",deprecated=True,)):results={"items":[{"item_id":"Foo"},{"item_id":"Bar"}]}ifq:results.update({"q":q})returnresults
В документации это будет отображено следующим образом:
Чтобы исключить query-параметр из генерируемой OpenAPI схемы (а также из системы автоматической генерации документации), укажите в Query параметр include_in_schema=False: