В FastAPI для работы с данными используется библиотека Pydantic, которая предоставляет удобные инструменты для валидации и сериализации данных. В этой статье мы погрузимся в более сложные аспекты работы с Pydantic: создание вложенных моделей, использование продвинутой валидации и управление конфигурацией приложения с помощью BaseSettings. Мы также создадим API с использованием вложенных Pydantic-моделей и параметров окружения.

Валидация в Pydantic

Pydantic делает валидацию данных простым и удобным процессом. Он автоматически проверяет типы данных, значения и даже позволяет нам использовать свои собственные методы валидации для более сложных сценариев.

Рассмотрим пример модели, которая принимает данные о пользователе и проверяет, что возраст находится в пределах от 18 до 100 лет:

from pydantic import BaseModel, validator

class User(BaseModel):
    name: str
    age: int

    @validator('age')
    def check_age(cls, v):
        if not (18 <= v <= 100):
            raise ValueError('Age must be between 18 and 100')
        return v

Пояснение к коду:

  • @validator(‘age’) — это декоратор, который добавляет валидацию для поля age.
  • Внутри метода мы проверяем, что возраст находится в пределах от 18 до 100 лет. Если условие не выполнено, мы выбрасываем ошибку.

Эти валидаторы можно использовать для любых типов данных, создавая более гибкие и точные схемы.

Создание сложных схем

Теперь давайте создадим более сложную модель с вложенными структурами. Например, нам нужно описать сущность заказа, который состоит из пользователя, товара и количества.

from pydantic import BaseModel
from typing import List

class Product(BaseModel):
    name: str
    price: float

class Order(BaseModel):
    user_name: str
    products: List[Product]
    total_amount: float

Пояснение к коду:

  • Product — это модель, которая описывает товар с именем и ценой.
  • Order — это модель для заказа, которая включает имя пользователя, список продуктов и общую сумму заказа.
  • Мы используем List[Product], чтобы указать, что заказ может включать несколько товаров.

Валидация вложенных данных

Pydantic автоматически валидирует вложенные модели, такие как список товаров. Например, если мы передадим неправильные данные (например, цену товара в виде строки), Pydantic выбросит ошибку валидации.

order_data = {
    "user_name": "John Doe",
    "products": [{"name": "Laptop", "price": "1000"}],  # Здесь ошибка: цена — строка, а не число
    "total_amount": 1000
}

order = Order(**order_data)  # Pydantic выбросит ошибку валидации

Настройка конфигурации моделей

Пример: Преобразование данных

Предположим, что мы хотим преобразовать все имена в модели в верхний регистр перед сохранением.

from pydantic import BaseModel

class User(BaseModel):
    name: str
    age: int

    class Config:
        # Это настройка для сериализации данных
        anystr_upper = True  # Преобразуем все строки в верхний регистр

Пояснение:

  • Config — это специальный класс, который используется для настройки поведения модели.
  • С помощью anystr_upper = True мы говорим, что все строки в модели должны быть преобразованы в верхний регистр.

Когда мы создадим объект User и выведем его данные, имя будет в верхнем регистре:

user = User(name="john doe", age=30)
print(user.name)  # JOHN DOE

Использование BaseSettings для управления конфигурацией приложения

BaseSettings — это специальный класс в Pydantic, предназначенный для управления параметрами конфигурации, которые обычно хранятся в файлах .env или переменных окружения. Это полезно, когда вам нужно настроить приложение в разных окружениях (например, в продакшн и в разработке).

Пример: Управление конфигурацией через переменные окружения

from pydantic import BaseSettings

class Settings(BaseSettings):
    app_name: str = "My FastAPI App"
    debug: bool = True
    database_url: str

    class Config:
        env_file = ".env"  # Путь к файлу с переменными окружения
        env_file_encoding = "utf-8"

# Загружаем настройки из файла .env
settings = Settings()
print(settings.app_name)
print(settings.debug)
print(settings.database_url)

Пояснение:

  • BaseSettings — это базовый класс для работы с настройками.
  • Мы создаем модель, которая загружает переменные окружения из файла .env. Например:
DATABASE_URL=postgresql://user:password@localhost/dbname

Практика: Создание API с вложенными Pydantic-моделями и настройкой параметров окружения

Шаг 1: Создадим файл .env:

DATABASE_URL=postgresql://user:password@localhost/mydatabase
DEBUG=True

Шаг 2: Реализуем API с использованием вложенных моделей и конфигурации:

from fastapi import FastAPI
from pydantic import BaseModel, BaseSettings
from typing import List

class Product(BaseModel):
    name: str
    price: float

class Order(BaseModel):
    user_name: str
    products: List[Product]
    total_amount: float

class Settings(BaseSettings):
    app_name: str = "Order API"
    debug: bool = True
    database_url: str

    class Config:
        env_file = ".env"

app = FastAPI()

# Загружаем настройки
settings = Settings()

@app.get("/orders/{order_id}")
def get_order(order_id: int):
    # Здесь мы могли бы подключиться к базе данных и получить заказ
    return {"order_id": order_id, "message": "Order details fetched successfully!"}

@app.post("/create-order/")
def create_order(order: Order):
    # Здесь мы могли бы сохранить заказ в базе данных
    return {"message": "Order created successfully!", "order": order.dict()}

if __name__ == "__main__":
    print(f"App Name: {settings.app_name}")
    print(f"Database URL: {settings.database_url}")

Заключение

Pydantic — мощный инструмент для работы с валидацией и сериализацией данных в FastAPI. Мы научились создавать сложные схемы, использовать продвинутую валидацию и управлять конфигурацией приложения через переменные окружения. Эти техники позволяют строить гибкие и надежные API, которые легко масштабируются и поддерживаются.