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(строка). Все параметры должны быть необязательными.