Appearance
FastAPI - Аннотации типов Path и Query.
Эта статья посвящена использованию аннотаций типов Path
и Query
в FastAPI для определения параметров API, улучшения валидации и автоматической генерации интерактивной документации Swagger UI.
python
from fastapi import FastAPI, Path, Query
from typing import Optional, List, Dict, Union
Зачем нужна аннотация типов?
Аннотации типов (type hints) были введены в Python 3.5 (PEP 484). Они служат нескольким важным целям:
- Улучшение читаемости кода: Аннотации типов сразу показывают, какой тип данных ожидает функция в качестве входных данных и какой тип она вернет. Это упрощает понимание и поддержку кода.
- Раннее обнаружение ошибок: Инструменты статического анализа (например, MyPy) и IDE (например, VS Code, PyCharm) могут использовать аннотации типов для обнаружения ошибок типов до запуска кода. Это помогает предотвратить ошибки.
- Улучшенная поддержка IDE: IDE используют аннотации типов для обеспечения лучшего автодополнения, подсказок по коду и возможностей рефакторинга.
- Основа для FastAPI: FastAPI требует аннотации типов для определения структуры вашего API. Они являются основным механизмом для определения параметров, валидации данных и генерации документации.
2. Тип аннотации Path
Path
- это класс, предоставляемый FastAPI специально для определения параметров пути. Параметры пути являются частью самого URL-адреса и обычно используются для идентификации конкретного ресурса.
Часто используемые параметры Path
:
...
(Многоточие): Это специальное значение, которое указывает, что параметр пути является обязательным. Если клиент не предоставит значение для обязательного параметра пути, FastAPI вернет ошибку 422 Unprocessable Entity.title: str
: Добавляет краткий заголовок/описание к параметру, который будет отображаться в документации Swagger UI.description: str
: Добавляет более длинное описание к параметру, также отображаемое в Swagger UI.ge: int | float
: "Больше или равно" (Greater than or equal to). Указывает минимальное значение для числового параметра пути.gt: int | float
: "Больше чем" (Greater than). Указывает значение, которое параметр должен строго превышать.le: int | float
: "Меньше или равно" (Less than or equal to). Указывает максимальное значение.lt: int | float
: "Меньше чем" (Less than). Указывает значение, которое параметр должен быть строго меньше.min_length: int
: Для строковых параметров пути указывает минимальную длину.max_length: int
: Для строковых параметров пути указывает максимальную длину.regex: str
: Для строковых параметров пути указывает регулярное выражение, которому должен соответствовать параметр.
Пример с Path
:
python
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(
item_id: int = Path(..., title="The ID of the item", ge=1, description="Must be greater or equal than 1")
):
"""
Retrieves an item by its ID.
Args:
item_id: The ID of the item (must be an integer greater than or equal to 1).
"""
return {"item_id": item_id}
@app.get("/users/{username}")
async def read_user(
username: str = Path(..., min_length=3, max_length=20, title="User name")
):
"""
Get user by username
Args:
username: user name, must be string with length between 3 and 20
"""
return {"username": username}
@app.get("/license-plates/{license_plate}")
async def read_license_plate(
license_plate: str = Path(..., regex=r"^[A-Z]{2}-\d{3}$", title="License plate UK format")
):
"""
Get license plate by UK format.
Args:
license_plate: License plate number. Format XX-999
"""
return {"license_plate": license_plate}
Пояснение:
item_id: int = Path(...)
:item_id: int
объявляет, чтоitem_id
является параметром пути и должен быть целым числом.= Path(...)
делает его обязательным параметром пути.title="The ID of the item"
предоставляет краткое описание для Swagger UI.ge=1
гарантирует, чтоitem_id
больше или равен 1. FastAPI автоматически проверит это.description
добавляет описание для Swagger UI.
username: str = Path(..., min_length=3, max_length=20)
:username: str
объявляет, чтоusername
является строковым параметром пути.min_length=3
иmax_length=20
обеспечивают ограничения по длине.
license_plate: str = Path(..., regex=r"^[A-Z]{2}-\d{3}$")
:regex
проверяет формат.
Swagger UI:
Когда вы запустите это приложение и откроете конечную точку /docs
, Swagger UI:
- Покажет
/items/{item_id}
и/users/{username}
как конечные точки. - Четко укажет, что
item_id
иusername
являются параметрами пути. - Отобразит
title
и любые другие метаданные, которые вы предоставили. - Покажет тип (целое число, строка) и любые ограничения (например,
ge=1
,min_length=3
). - Позволит вам "Try it out", введя значение для
item_id
иusername
и отправив запрос. FastAPI автоматически проверит ввод на основе вашей конфигурацииPath
.
Тип аннотации Query
Query
похож на Path
, но используется для определения параметров запроса. Параметры запроса добавляются к URL-адресу после вопросительного знака (?
) и обычно используются для фильтрации, сортировки или пагинации.
Часто используемые параметры Query
:
Query
поддерживает все те же параметры, что и Path
(title
, description
, ge
, gt
, le
, lt
, min_length
, max_length
, regex
), а также:
default: Any
: Указывает значение по умолчанию для параметра запроса. Если клиент не предоставит значение, будет использовано значение по умолчанию. Еслиdefault
не указан, а тип не является необязательным, параметр обязателен.alias: str
: Позволяет использовать другое имя для параметра запроса в вашем коде Python, чем имя, которое появляется в URL.deprecated: bool
: Помечает параметр запроса как устаревший в документации Swagger UI.
Пример с Query
:
python
from fastapi import FastAPI, Query
from typing import Optional, List
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Optional[str] = Query(None, title="Search query", max_length=50),
skip: int = Query(0, title="Number of items to skip", ge=0),
limit: int = Query(10, title="Maximum number of items to return", le=100),
sort_by: Optional[List[str]] = Query(None, title="Fields to sort by")
):
"""
Retrieves a list of items.
Args:
q: Optional search query (string, max length 50).
skip: Number of items to skip (integer, >= 0).
limit: Maximum number of items to return (integer, <= 100).
sort_by: list of fields to sort
"""
# In a real application, you would use these parameters to query a database.
results = [
{"item_id": 1, "name": "Bad"},
{"item_id": 2, "name": "Bar"},
{"item_id": 3, "name": "Baz"}
]
if q:
results = [item for item in results if q.lower() in item["name"].lower()]
if sort_by:
# Basic sort implementation (for demonstration purposes)
for field in reversed(sort_by): # Apply sorts in reverse order
results.sort(key=lambda item: item.get(field, ""))
return results[skip : skip + limit]
@app.get("/cars/")
async def read_cars(
color: Optional[str] = Query(None, alias="car-color", title="Car's color"),
year: int = Query(..., gt=1900, title="Manufacture year")
):
"""
Retrieves cars based on color and year.
Args:
color: The color of the car (optional, uses alias 'car-color' in the URL).
year: The manufacture year (required, must be greater than 1900).
"""
return {"color": color, "year": year}
Пояснение:
q: Optional[str] = Query(None, ...)
:Optional[str]
означает, что параметрq
является необязательным строковым параметром.= Query(None, ...)
устанавливает значение по умолчаниюNone
, если параметр не предоставлен.max_length=50
ограничивает длину строки.
skip: int = Query(0, ...)
:skip
- обязательный целочисленный параметр (потому что указано значение по умолчанию0
).ge=0
гарантирует, чтоskip
неотрицателен.
limit: int = Query(10, ...)
:limit
- обязательный целочисленный параметр (по умолчанию 10).le=100
ограничивает максимальное значение.
sort_by: Optional[List[str]] = Query(None, ...)
:sort_by
- Необязательный параметр, который принимает список строк.
color: Optional[str] = Query(None, alias="car-color", ...)
:alias="car-color"
означает, что в URL-адресе будет использоваться имяcar-color
, но в коде Python вы будете обращаться к этому параметру какcolor
.
year: int = Query(..., gt=1900, ...)
:year
- обязательный целочисленный параметр (многоточие...
).gt=1900
- год должен быть больше 1900.
Swagger UI:
- Покажет
/items/
и/cars/
как конечные точки. - Для
/items/
:- Отобразит
q
,skip
,limit
иsort_by
как параметры запроса. - Покажет их типы, описания, значения по умолчанию и ограничения.
- Отобразит
- Для
/cars/
:- Отобразит
car-color
(из-заalias
) иyear
как параметры запроса. - Покажет, что
car-color
необязателен, аyear
обязателен.
- Отобразит
- "Try it out" позволит вам вводить значения для параметров запроса и отправлять запросы.
Сравнение оформления:
1. Без аннотаций типов:
python
from fastapi import FastAPI
app = FastAPI()
@app.get("/items_no_types")
async def read_items_no_types(item_id, q): # No type hints!
return {"item_id": item_id, "q": q}
- Swagger UI: Покажет
item_id
иq
как параметры, но без указания их типов. Swagger UI определит их как "string", но это не совсем точно. Нет никакой валидации. - Проблемы: Нет валидации типов. Любые входные данные будут приняты, что может привести к ошибкам внутри вашей функции. IDE не сможет предоставить полезные подсказки.
2. С аннотациями базовых типов:
python
from fastapi import FastAPI
from typing import Optional
app = FastAPI()
@app.get("/items_basic_types")
async def read_items_basic_types(item_id: int, q: Optional[str]):
return {"item_id": item_id, "q": q}
- Swagger UI: Покажет
item_id
как integer, аq
как string (и как необязательный параметр). Это уже намного лучше! - Преимущества: FastAPI выполнит базовую проверку типов. Если вы попытаетесь передать строку в
item_id
, вы получите ошибку. IDE предоставит лучшие подсказки.
3. С аннотациями Path
и Query
:
python
from fastapi import FastAPI, Path, Query
from typing import Optional
app = FastAPI()
@app.get("/items_with_path_query/{item_id}")
async def read_items_with_path_query(
item_id: int = Path(..., title="Item ID", ge=1),
q: Optional[str] = Query(None, title="Query String", max_length=50)
):
return {"item_id": item_id, "q": q}
- Swagger UI: Наиболее информативный вариант. Покажет
item_id
как параметр пути, аq
как параметр запроса. Отобразит все метаданные (title, description, ограничения). "Try it out" будет работать максимально корректно. - Преимущества: Полная валидация, лучшая документация, лучший опыт разработки.
Упражнения:
"Добавление пользователя" (Add User): Создайте endpoint
/users/{user_id}
(GET), который принимаетuser_id
как параметр пути (целое число, больше 0). Добавьте параметр запросаis_active
(булево значение, по умолчаниюTrue
)."Поиск книг" (Book Search): Создайте endpoint
/books/
(GET), который принимает параметры запроса:title
(строка, необязательный),author
(строка, необязательный),min_pages
(целое число, по умолчанию 0),max_pages
(целое число, необязательный)."Валидация ISBN" (ISBN Validation): Создайте endpoint
/books/{isbn}
(GET), который принимаетisbn
как параметр пути (строка). Используйтеregex
вPath
, чтобы проверить, чтоisbn
соответствует формату ISBN-10 или ISBN-13 (найдите регулярные выражения для ISBN в Интернете)."Устаревший параметр" (Deprecated Parameter): Добавьте к endpoint
/books/
параметр запросаsort_order
(строка, по умолчанию "asc"). Пометьте его как устаревший (deprecated=True
). Проверьте, как это отображается в Swagger UI."Псевдоним параметра" (Parameter Alias): Добавьте к endpoint
/users/{user_id}
параметр запросаemail_address
, но используйтеalias
так, чтобы в URL-адресе он называлсяemail
."Список с ограничениями"(Constrained List): Создайте endpoint
/tags/
(GET), который принимает параметр запросаtags
(список строк). Ограничьте максимальное количество тегов до 5."Сложный фильтр" (Complex Filter): Создайте endpoint
/products/
(GET) с параметрами запросаmin_price
(число с плавающей точкой),max_price
(число с плавающей точкой) иcategory
(строка). Все параметры должны быть необязательными.