Query параметри та валідація рядків¶
FastAPI дозволяє оголошувати додаткову інформацію та виконувати валідацію для ваших параметрів.
Розглянемо цей додаток як приклад:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/")
async def read_items(q: str | None = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
🤓 Other versions and variants
from typing import Union
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/")
async def read_items(q: Union[str, None] = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Query параметр q має тип str | None, що означає, що він має тип str, але також може бути None, і справді, значення за замовчуванням — None, тож FastAPI знатиме, що він не є обов'язковим.
Примітка
FastAPI знатиме, що значення q не є обов’язковим, завдяки значенню за замовчуванням = None.
Використання str | None дозволить вашому редактору коду надавати кращу підтримку та виявляти помилки.
Додаткова валідація¶
Ми хочемо, щоб навіть якщо q є необов’язковим, коли його передають, його довжина не перевищувала 50 символів.
Імпорт Query та Annotated¶
Щоб це зробити, спочатку імпортуємо:
QueryзfastapiAnnotatedзtyping
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(max_length=50)] = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
🤓 Other versions and variants
from typing import Annotated, Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[Union[str, None], Query(max_length=50)] = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Tip
Prefer to use the Annotated version if possible.
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str | None = Query(default=None, max_length=50)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Tip
Prefer to use the Annotated version if possible.
from typing import Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Union[str, None] = Query(default=None, max_length=50)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Інформація
FastAPI додав підтримку Annotated (і почав рекомендувати його) у версії 0.95.0.
Якщо у вас старіша версія, під час використання Annotated можуть виникати помилки.
Переконайтеся, що ви оновили версію FastAPI до принаймні 0.95.1, перш ніж використовувати Annotated.
Використання Annotated у типі параметра q¶
Пам’ятаєте, як я раніше розповідав, що 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.
А тепер переходимо до цікавого! 🎉
Додавання Query до Annotated у параметр q¶
Тепер, коли у нас є Annotated, де ми можемо додавати додаткову інформацію (у цьому випадку — додаткову валідацію), додамо Query всередину Annotated і встановимо параметр max_length у 50:
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(max_length=50)] = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
🤓 Other versions and variants
from typing import Annotated, Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[Union[str, None], Query(max_length=50)] = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Tip
Prefer to use the Annotated version if possible.
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str | None = Query(default=None, max_length=50)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Tip
Prefer to use the Annotated version if possible.
from typing import Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Union[str, None] = Query(default=None, max_length=50)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Зверніть увагу, що значення за замовчуванням усе ще None, тому параметр залишається необов'язковим.
Але тепер, додавши Query(max_length=50) всередину Annotated, ми повідомляємо FastAPI, що хочемо додаткову валідацію для цього значення: ми хочемо, щоб воно мало максимум 50 символів. 😎
Порада
Тут ми використовуємо Query(), оскільки це query параметр. Далі ми розглянемо інші варіанти, як-от Path(), Body(), Header() та Cookie(), які приймають ті самі аргументи, що й Query().
Тепер FastAPI:
- Перевірить дані, щоб переконатися, що їхня максимальна довжина — 50 символів
- Покажe чітку помилку клієнту, якщо дані недійсні
- Задокументує параметр в OpenAPI-схемі операції шляху (що відобразиться в автоматично згенерованій документації)
Альтернативний (застарілий) метод: Query як значення за замовчуванням¶
У попередніх версіях FastAPI (до 0.95.0) потрібно було використовувати Query як значення за замовчуванням параметра, замість того, щоб додавати його в Annotated. Є висока ймовірність, що ви зустрінете код із таким підходом, тож я поясню його.
Порада
Для нового коду та коли це можливо, використовуйте Annotated, як показано вище. Це має багато переваг (пояснених нижче) і не має недоліків. 🍰
Раніше ми писали Query() як значення за замовчуванням для параметра функції, встановлюючи max_length у 50:
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str | None = Query(default=None, max_length=50)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
🤓 Other versions and variants
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(max_length=50)] = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
from typing import Annotated, Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[Union[str, None], Query(max_length=50)] = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Tip
Prefer to use the Annotated version if possible.
from typing import Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Union[str, None] = Query(default=None, max_length=50)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Оскільки в цьому випадку (без Annotated) нам потрібно замінити значення за замовчуванням None у функції на Query(), тепер ми повинні встановити значення за замовчуванням через параметр Query(default=None). Це виконує ту саму роль визначення значення за замовчуванням (принаймні для FastAPI).
Таким чином:
q: str | None = Query(default=None)
...робить параметр необов’язковим зі значенням за замовчуванням None, що еквівалентно:
q: str | None = None
Але у версії з Query ми явно вказуємо, що це query параметр.
Далі ми можемо передавати Query додаткові параметри. У цьому випадку — параметр max_length, який застосовується до рядків:
q: str | None = Query(default=None, max_length=50)
Це забезпечить валідацію даних, виведе зрозумілу помилку у разі недійсних даних і задокументує параметр у схемі OpenAPI операції шляху.
Query як значення за замовчуванням або всередині Annotated¶
Важливо пам’ятати, якщо використовувати Query всередині Annotated, не можна задавати параметр default у Query.
Замість цього використовуйте фактичне значення за замовчуванням параметра функції. Інакше це буде непослідовно.
Наприклад, цей варіант є некоректним:
q: Annotated[str, Query(default="rick")] = "morty"
...тому, що не зрозуміло, яке значення має бути значенням за замовчуванням: "rick" чи "morty".
Тож ви будете використовувати (бажано):
q: Annotated[str, Query()] = "rick"
...або у старих кодових базах ви знайдете:
q: str = Query(default="rick")
Переваги використання Annotated¶
Використання Annotated є рекомендованим замість задання значення за замовчуванням у параметрах функції, оскільки воно краще з кількох причин. 🤓
Значення за замовчуванням параметра функції є фактичним значенням за замовчуванням, що є більш інтуїтивним у Python загалом. 😌
Ви можете викликати ту саму функцію в інших місцях без FastAPI, і вона працюватиме очікувано. Якщо параметр є обов’язковим (без значення за замовчуванням), ваш редактор повідомить про помилку, а Python також видасть помилку, якщо ви виконаєте функцію без передавання цього параметра.
Якщо ви не використовуєте Annotated, а використовуєте (старий) стиль значень за замовчуванням, то при виклику цієї функції без FastAPI в інших місцях, потрібно пам’ятати передати їй аргументи, щоб вона працювала коректно, інакше значення будуть відрізнятися від очікуваних (наприклад, ви отримаєте QueryInfo або щось подібне замість str). І ваш редактор не повідомить про помилку, і Python не скаржитиметься під час запуску цієї функції — лише коли операції всередині завершаться помилкою.
Оскільки Annotated може містити кілька анотацій метаданих, тепер ви навіть можете використовувати ту саму функцію з іншими інструментами, такими як Typer. 🚀
Додавання додаткових валідацій¶
Ви також можете додати параметр min_length:
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Annotated[str | None, Query(min_length=3, max_length=50)] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
🤓 Other versions and variants
from typing import Annotated, Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Annotated[Union[str, None], Query(min_length=3, max_length=50)] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Tip
Prefer to use the Annotated version if possible.
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str | None = Query(default=None, min_length=3, max_length=50)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Tip
Prefer to use the Annotated version if possible.
from typing import Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Union[str, None] = Query(default=None, min_length=3, max_length=50),
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Додавання регулярних виразів¶
Ви можете визначити regular expression pattern, якому має відповідати параметр:
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Annotated[
str | None, Query(min_length=3, max_length=50, pattern="^fixedquery$")
] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
🤓 Other versions and variants
from typing import Annotated, Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Annotated[
Union[str, None], Query(min_length=3, max_length=50, pattern="^fixedquery$")
] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Tip
Prefer to use the Annotated version if possible.
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: str | None = Query(
default=None, min_length=3, max_length=50, pattern="^fixedquery$"
),
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Tip
Prefer to use the Annotated version if possible.
from typing import Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Union[str, None] = Query(
default=None, min_length=3, max_length=50, pattern="^fixedquery$"
),
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Цей конкретний шаблон регулярного виразу перевіряє, що отримане значення параметра:
^: починається з наступних символів, перед якими немає інших символів.fixedquery: точно відповідає значеннюfixedquery.$: закінчується тут, післяfixedqueryнемає жодних символів.
Якщо ви почуваєтеся розгублено щодо «regular expression», не хвилюйтеся. Це складна тема для багатьох людей. Ви все одно можете робити багато речей без використання регулярних виразів.
Тепер ви знаєте, що коли вони знадобляться, їх можна застосовувати у FastAPI.
Значення за замовчуванням¶
Ви можете, звісно, використовувати значення за замовчуванням, відмінні від None.
Припустімо, що ви хочете оголосити query параметр q з min_length 3 і значенням за замовчуванням "fixedquery":
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)] = "fixedquery"):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str = Query(default="fixedquery", min_length=3)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Примітка
Наявність значення за замовчуванням будь-якого типу, включаючи None, робить параметр необов’язковим (not required).
Обов’язкові параметри¶
Якщо нам не потрібно оголошувати додаткові валідації або метадані, ми можемо зробити query параметр q обов’язковим, просто не вказуючи значення за замовчуванням, наприклад:
q: str
замість:
q: str | None = None
Але тепер ми оголошуємо його з Query, наприклад так:
q: Annotated[str | None, Query(min_length=3)] = None
Тому, якщо вам потрібно оголосити значення як обов’язкове під час використання Query, просто не вказуйте значення за замовчуванням:
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)]):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str = Query(min_length=3)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Обов’язковий, може бути None¶
Ви можете вказати, що параметр може приймати None, але при цьому залишається обов’язковим. Це змусить клієнтів надіслати значення, навіть якщо значення дорівнює None.
Щоб зробити це, оголосіть, що None є допустимим типом, але просто не вказуйте значення за замовчуванням:
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(min_length=3)]):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
🤓 Other versions and variants
from typing import Annotated, Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[Union[str, None], Query(min_length=3)]):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Tip
Prefer to use the Annotated version if possible.
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str | None = Query(min_length=3)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Tip
Prefer to use the Annotated version if possible.
from typing import Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Union[str, None] = Query(min_length=3)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Список query параметрів / кілька значень¶
Коли ви явно визначаєте query параметр за допомогою Query, ви також можете оголосити, що він має приймати список значень, або, іншими словами, кілька значень.
Наприклад, щоб оголосити query параметр q, який може з’являтися в URL кілька разів, можна написати:
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[list[str] | None, Query()] = None):
query_items = {"q": q}
return query_items
🤓 Other versions and variants
from typing import Annotated, Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[Union[list[str], None], Query()] = None):
query_items = {"q": q}
return query_items
Tip
Prefer to use the Annotated version if possible.
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: list[str] | None = Query(default=None)):
query_items = {"q": q}
return query_items
Tip
Prefer to use the Annotated version if possible.
from typing import Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Union[list[str], None] = Query(default=None)):
query_items = {"q": q}
return query_items
Тоді, у випадку URL:
http://localhost:8000/items/?q=foo&q=bar
ви отримаєте кілька значень q query параметрів (foo і bar) у вигляді Python list у вашій функції операції шляху, у параметрі функції q.
Отже, відповідь на цей URL буде:
{
"q": [
"foo",
"bar"
]
}
Порада
Щоб оголосити query параметр з типом list, як у наведеному вище прикладі, потрібно явно використовувати Query, інакше він буде інтерпретований як тіло запиту.
Інтерактивна API-документація оновиться відповідно, дозволяючи передавати кілька значень:

Список query параметрів / кілька значень за замовчуванням¶
Ви також можете визначити значення за замовчуванням list, якщо жодне значення не було передане:
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[list[str], Query()] = ["foo", "bar"]):
query_items = {"q": q}
return query_items
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: list[str] = Query(default=["foo", "bar"])):
query_items = {"q": q}
return query_items
Якщо ви перейдете за посиланням:
http://localhost:8000/items/
то значення q за замовчуванням буде: ["foo", "bar"], і ваша відповідь виглядатиме так:
{
"q": [
"foo",
"bar"
]
}
Використання тільки list¶
Ви також можете використовувати list напряму замість list[str]:
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[list, Query()] = []):
query_items = {"q": q}
return query_items
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: list = Query(default=[])):
query_items = {"q": q}
return query_items
Примітка
Майте на увазі, що в цьому випадку FastAPI не перевірятиме вміст списку.
Наприклад, list[int] перевірятиме (і документуватиме), що вміст списку — цілі числа. Але list без уточнення цього не робитиме.
Оголосити більше метаданих¶
Ви можете додати більше інформації про параметр.
Ця інформація буде включена у згенерований OpenAPI та використана інтерфейсами документації та зовнішніми інструментами.
Примітка
Майте на увазі, що різні інструменти можуть мати різний рівень підтримки OpenAPI.
Деякі з них можуть ще не відображати всю додаткову інформацію, хоча в більшості випадків відсутню функцію вже заплановано до реалізації.
Ви можете додати title:
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Annotated[str | None, Query(title="Query string", min_length=3)] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
🤓 Other versions and variants
from typing import Annotated, Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Annotated[Union[str, None], Query(title="Query string", min_length=3)] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Tip
Prefer to use the Annotated version if possible.
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: str | None = Query(default=None, title="Query string", min_length=3),
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Tip
Prefer to use the Annotated version if possible.
from typing import Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Union[str, None] = Query(default=None, title="Query string", min_length=3),
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
А також description:
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_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"}]}
if q:
results.update({"q": q})
return results
🤓 Other versions and variants
from typing import Annotated, Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_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"}]}
if q:
results.update({"q": q})
return results
Tip
Prefer to use the Annotated version if possible.
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_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"}]}
if q:
results.update({"q": q})
return results
Tip
Prefer to use the Annotated version if possible.
from typing import Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_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"}]}
if q:
results.update({"q": q})
return results
Аліаси параметрів¶
Уявіть, що ви хочете, щоб параметр називався item-query.
Наприклад:
http://127.0.0.1:8000/items/?item-query=foobaritems
Але item-query — це некоректна назва змінної в Python.
Найближчий допустимий варіант — item_query.
Проте вам потрібно, щоб параметр залишався саме item-query...
У такому випадку можна оголосити alias, і саме він буде використовуватися для отримання значення параметра:
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(alias="item-query")] = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
🤓 Other versions and variants
from typing import Annotated, Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Annotated[Union[str, None], Query(alias="item-query")] = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Tip
Prefer to use the Annotated version if possible.
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str | None = Query(default=None, alias="item-query")):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Tip
Prefer to use the Annotated version if possible.
from typing import Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Union[str, None] = Query(default=None, alias="item-query")):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Позначення параметрів як застарілих¶
Припустімо, що вам більше не подобається цей параметр.
Вам потрібно залишити його на деякий час, оскільки ним користуються клієнти, але ви хочете, щоб документація чітко показувала, що він є deprecated.
Тоді передайте параметр deprecated=True до Query:
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_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"}]}
if q:
results.update({"q": q})
return results
🤓 Other versions and variants
from typing import Annotated, Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_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"}]}
if q:
results.update({"q": q})
return results
Tip
Prefer to use the Annotated version if possible.
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_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"}]}
if q:
results.update({"q": q})
return results
Tip
Prefer to use the Annotated version if possible.
from typing import Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_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"}]}
if q:
results.update({"q": q})
return results
Документація буде показувати це таким чином:

Виняток параметрів з OpenAPI¶
Щоб виключити query параметр зі згенерованої схеми OpenAPI (і, таким чином, з автоматичних систем документації), встановіть параметр include_in_schema для Query в False:
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
hidden_query: Annotated[str | None, Query(include_in_schema=False)] = None,
):
if hidden_query:
return {"hidden_query": hidden_query}
else:
return {"hidden_query": "Not found"}
🤓 Other versions and variants
from typing import Annotated, Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
hidden_query: Annotated[Union[str, None], Query(include_in_schema=False)] = None,
):
if hidden_query:
return {"hidden_query": hidden_query}
else:
return {"hidden_query": "Not found"}
Tip
Prefer to use the Annotated version if possible.
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
hidden_query: str | None = Query(default=None, include_in_schema=False),
):
if hidden_query:
return {"hidden_query": hidden_query}
else:
return {"hidden_query": "Not found"}
Tip
Prefer to use the Annotated version if possible.
from typing import Union
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
hidden_query: Union[str, None] = Query(default=None, include_in_schema=False),
):
if hidden_query:
return {"hidden_query": hidden_query}
else:
return {"hidden_query": "Not found"}
Кастомна валідація¶
Можуть бути випадки, коли вам потрібно провести кастомну валідацію, яку не можна реалізувати за допомогою параметрів, показаних вище.
У таких випадках ви можете використати кастомну функцію-валідатор, яка буде застосована після звичайної валідації (наприклад, після перевірки, що значення є типом str).
Це можна досягти за допомогою Pydantic's AfterValidator в середині Annotated.
Порада
Pydantic також має BeforeValidator та інші. 🤓
Наприклад, цей кастомний валідатор перевіряє, чи починається ID елемента з isbn- для номера книги ISBN або з imdb- для ID URL фільму на IMDB:
import random
from typing import Annotated
from fastapi import FastAPI
from pydantic import AfterValidator
app = FastAPI()
data = {
"isbn-9781529046137": "The Hitchhiker's Guide to the Galaxy",
"imdb-tt0371724": "The Hitchhiker's Guide to the Galaxy",
"isbn-9781439512982": "Isaac Asimov: The Complete Stories, Vol. 2",
}
def check_valid_id(id: str):
if not id.startswith(("isbn-", "imdb-")):
raise ValueError('Invalid ID format, it must start with "isbn-" or "imdb-"')
return id
@app.get("/items/")
async def read_items(
id: Annotated[str | None, AfterValidator(check_valid_id)] = None,
):
if id:
item = data.get(id)
else:
id, item = random.choice(list(data.items()))
return {"id": id, "name": item}
🤓 Other versions and variants
import random
from typing import Annotated, Union
from fastapi import FastAPI
from pydantic import AfterValidator
app = FastAPI()
data = {
"isbn-9781529046137": "The Hitchhiker's Guide to the Galaxy",
"imdb-tt0371724": "The Hitchhiker's Guide to the Galaxy",
"isbn-9781439512982": "Isaac Asimov: The Complete Stories, Vol. 2",
}
def check_valid_id(id: str):
if not id.startswith(("isbn-", "imdb-")):
raise ValueError('Invalid ID format, it must start with "isbn-" or "imdb-"')
return id
@app.get("/items/")
async def read_items(
id: Annotated[Union[str, None], AfterValidator(check_valid_id)] = None,
):
if id:
item = data.get(id)
else:
id, item = random.choice(list(data.items()))
return {"id": id, "name": item}
Інформація
Це доступно з версії Pydantic 2 або вище. 😎
Порада
Якщо вам потрібно виконати будь-яку валідацію, яка вимагає взаємодії з будь-яким зовнішнім компонентом, таким як база даних чи інший API, замість цього слід використовувати FastAPI Dependencies — ви дізнаєтесь про них пізніше.
Ці кастомні валідатори використовуються для речей, які можна перевірити лише з тіими самими даними, що надані в запиті.
Зрозумійте цей код¶
Головний момент — це використання AfterValidator з функцією всередині Annotated. Можете пропустити цю частину, якщо хочете. 🤸
Але якщо вам цікаво розібратися в цьому конкретному прикладі коду і вам ще не набридло, ось кілька додаткових деталей.
Рядок із value.startswith()¶
Звернули увагу? Рядок із value.startswith() може приймати кортеж, і тоді він перевірятиме кожне значення в кортежі:
# Code above omitted 👆
def check_valid_id(id: str):
if not id.startswith(("isbn-", "imdb-")):
raise ValueError('Invalid ID format, it must start with "isbn-" or "imdb-"')
return id
# Code below omitted 👇
👀 Full file preview
import random
from typing import Annotated
from fastapi import FastAPI
from pydantic import AfterValidator
app = FastAPI()
data = {
"isbn-9781529046137": "The Hitchhiker's Guide to the Galaxy",
"imdb-tt0371724": "The Hitchhiker's Guide to the Galaxy",
"isbn-9781439512982": "Isaac Asimov: The Complete Stories, Vol. 2",
}
def check_valid_id(id: str):
if not id.startswith(("isbn-", "imdb-")):
raise ValueError('Invalid ID format, it must start with "isbn-" or "imdb-"')
return id
@app.get("/items/")
async def read_items(
id: Annotated[str | None, AfterValidator(check_valid_id)] = None,
):
if id:
item = data.get(id)
else:
id, item = random.choice(list(data.items()))
return {"id": id, "name": item}
🤓 Other versions and variants
import random
from typing import Annotated, Union
from fastapi import FastAPI
from pydantic import AfterValidator
app = FastAPI()
data = {
"isbn-9781529046137": "The Hitchhiker's Guide to the Galaxy",
"imdb-tt0371724": "The Hitchhiker's Guide to the Galaxy",
"isbn-9781439512982": "Isaac Asimov: The Complete Stories, Vol. 2",
}
def check_valid_id(id: str):
if not id.startswith(("isbn-", "imdb-")):
raise ValueError('Invalid ID format, it must start with "isbn-" or "imdb-"')
return id
@app.get("/items/")
async def read_items(
id: Annotated[Union[str, None], AfterValidator(check_valid_id)] = None,
):
if id:
item = data.get(id)
else:
id, item = random.choice(list(data.items()))
return {"id": id, "name": item}
Випадковий елемент¶
За допомогою data.items() ми отримуємо iterable object із кортежами, що містять ключ і значення для кожного елемента словника.
Ми перетворюємо цей iterable object у звичайний list за допомогою list(data.items()).
Потім, використовуючи random.choice(), ми можемо отримати випадкове значення зі списку, тобто отримуємо кортеж із (id, name). Це може бути щось на зразок ("imdb-tt0371724", "The Hitchhiker's Guide to the Galaxy").
Далі ми присвоюємо ці два значення кортежу змінним id і name.
Тож, якщо користувач не вказав ID елемента, він все одно отримає випадкову рекомендацію.
...ми робимо все це в одному простому рядку. 🤯 Хіба ви не любите Python? 🐍
# Code above omitted 👆
@app.get("/items/")
async def read_items(
id: Annotated[str | None, AfterValidator(check_valid_id)] = None,
):
if id:
item = data.get(id)
else:
id, item = random.choice(list(data.items()))
return {"id": id, "name": item}
👀 Full file preview
import random
from typing import Annotated
from fastapi import FastAPI
from pydantic import AfterValidator
app = FastAPI()
data = {
"isbn-9781529046137": "The Hitchhiker's Guide to the Galaxy",
"imdb-tt0371724": "The Hitchhiker's Guide to the Galaxy",
"isbn-9781439512982": "Isaac Asimov: The Complete Stories, Vol. 2",
}
def check_valid_id(id: str):
if not id.startswith(("isbn-", "imdb-")):
raise ValueError('Invalid ID format, it must start with "isbn-" or "imdb-"')
return id
@app.get("/items/")
async def read_items(
id: Annotated[str | None, AfterValidator(check_valid_id)] = None,
):
if id:
item = data.get(id)
else:
id, item = random.choice(list(data.items()))
return {"id": id, "name": item}
🤓 Other versions and variants
import random
from typing import Annotated, Union
from fastapi import FastAPI
from pydantic import AfterValidator
app = FastAPI()
data = {
"isbn-9781529046137": "The Hitchhiker's Guide to the Galaxy",
"imdb-tt0371724": "The Hitchhiker's Guide to the Galaxy",
"isbn-9781439512982": "Isaac Asimov: The Complete Stories, Vol. 2",
}
def check_valid_id(id: str):
if not id.startswith(("isbn-", "imdb-")):
raise ValueError('Invalid ID format, it must start with "isbn-" or "imdb-"')
return id
@app.get("/items/")
async def read_items(
id: Annotated[Union[str, None], AfterValidator(check_valid_id)] = None,
):
if id:
item = data.get(id)
else:
id, item = random.choice(list(data.items()))
return {"id": id, "name": item}
Підсумок¶
Ви можете оголошувати додаткові валідації та метадані для ваших параметрів.
Загальні валідації та метадані:
aliastitledescriptiondeprecated
Валідації, специфічні для рядків:
min_lengthmax_lengthpattern
Кастомні валідації за допомогою AfterValidator.
У цих прикладах ви побачили, як оголошувати валідації для значень str.
Дивіться наступні розділи, щоб дізнатися, як оголошувати валідації для інших типів, наприклад чисел.