Введение в Pydantic: Основы и продвинутые возможности
Введение
Что такое Pydantic?
В современном программировании, особенно в разработке на Python, важно эффективно управлять данными. От качества обработки данных зависит стабильность и безопасность приложений. Здесь на сцену выходит Pydantic — библиотека, которая предлагает довольно простой способ валидации и сериализации данных.
Pydantic создан для того, чтобы облегчить разработчикам работу с данными, делая её более предсказуемой и безопасной. Основная цель библиотеки — обеспечить строгую проверку типов данных и их валидацию с минимальными усилиями со стороны программиста. Pydantic использует возможности аннотаций типов Python для описания моделей данных и автоматически генерирует валидаторы, которые проверяют данные на соответствие этим аннотациям.
Основные возможности Pydantic:
1. Модели данных: Pydantic предоставляет удобный способ описания моделей данных через классы Python. Каждая модель представляет собой набор полей с определёнными типами данных и возможными значениями по умолчанию. Это позволяет явно описывать структуру данных и требования к ним.
from pydantic import BaseModel
class User(BaseModel):
id: int
name: str
age: int = 18
2. Валидация данных: Одна из ключевых возможностей Pydantic — автоматическая валидация данных. При создании экземпляра модели все переданные данные проверяются на соответствие типам, указанным в аннотациях. Если данные не соответствуют требованиям, Pydantic генерирует информативные сообщения об ошибках.
user = User(id='first', name='Alice', age=25)
3. Конвертация данных: Помимо валидации, Pydantic автоматически преобразует данные в нужные типы. Например, если поле ожидает значение типа int
, а ему передана строка, представляющая число, Pydantic предпримет попытку конвертации.
user = User(id='1', name='Alice', age='25')
print(user.id) # 1
print(user.age) # 25
4. Работа с вложенными моделями: Pydantic поддерживает вложенные модели, что позволяет создавать сложные структуры данных. Это особенно полезно при работе с JSON и другими форматами данных, где часто встречаются вложенные объекты.
class Address(BaseModel):
street: str
city: str
class UserWithAddress(BaseModel):
id: int
name: str
address: Address
address = Address(street='123 Main St', city='New York')
user = UserWithAddress(id=1, name='Alice', address=address)
5. Валидация пользовательских типов данных: Pydantic позволяет создавать и использовать кастомные валидаторы для проверки данных, что даёт большую гибкость и контроль.
from pydantic import validator
class UserWithValidation(BaseModel):
id: int
name: str
age: int
@validator('age')
def check_age(cls, v):
if v < 18:
raise ValueError('Age must be at least 18')
return v
user = UserWithValidation(id=1, name='Alice', age=25)
6. Интеграция с другими библиотеками и фреймворками: Pydantic легко интегрируется с популярными фреймворками, такими как FastAPI, что делает его отличным выбором для разработки веб-приложений. Например, в FastAPI модели Pydantic используются для валидации запросов и ответов, что существенно упрощает разработку и улучшает качество кода.
Краткая история создания Pydantic
Pydantic появился на свет благодаря стремлению разработчиков упростить и улучшить процесс валидации данных в Python. Основателем и главным разработчиком библиотеки является Сэмюэль Коллинсон (Samuel Colvin). Изначальная версия Pydantic была выпущена в 2018 году и быстро завоевала популярность среди разработчиков благодаря своей простоте и эффективности.
Идея создания Pydantic возникла из необходимости более строгой и надежной валидации данных в проектах, где неправильная обработка данных может привести к критическим ошибкам. На фоне растущей популярности аннотаций типов в Python, Pydantic предложил удобный способ использования этих аннотаций для создания моделей данных и автоматической проверки их корректности.
С момента своего появления, Pydantic прошёл через множество изменений и улучшений. Ниже приведены некоторые ключевые обновления:
Версия 1.0 (2018 год)
- Первая стабильная версия, включающая базовые функции валидации и сериализации данных.
- Поддержка базовых типов данных и аннотаций типов.
- Возможность создания простых моделей данных с валидацией.
Версия 1.4 (2019 год)
- Введение поддержки вложенных моделей и более сложных структур данных.
- Улучшение производительности и расширение функциональности кастомных валидаторов.
- Интеграция с FastAPI, что позволило использовать Pydantic для создания API с автоматической валидацией.
Версия 1.7 (2020 год)
- Добавление новых типов данных, таких как UUID, Decimal и IPvAnyAddress.
- Улучшение сообщений об ошибках валидации, что сделало их более информативными и понятными.
- Оптимизация производительности и уменьшение времени обработки данных.
Версия 1.8 (2021 год)
- Расширение возможностей для работы с конфигурационными файлами и настройками моделей.
- Улучшение работы с datetime объектами и поддержка различных форматов дат и времени.
- Введение поддержки динамического создания моделей с помощью функции
create_model
.
Версия 2.2 (2023 год)
- Переписанный на Rust модуль pydantic-core для валидации, что значительно повысило производительность.
- Поддержка Python 3.8 и выше.
- Новый интерфейс для конфигурации моделей.
- Улучшенная система сообщений об ошибках, что делает их более информативными () ().
Версия 2.7 (2024 год)
- Введение опции serialize_as_any для сериализации объектов с произвольными полями.
- Возможность передавать контекст в процессе сериализации для более гибкой обработки данных.
- Улучшения производительности и новые функции, такие как расширенные возможности работы с аннотациями типов и оптимизация процесса валидации данных.
Последняя версия Pydantic включает множество новых возможностей и улучшений, что делает библиотеку ещё более мощной и гибкой. Некоторые из наиболее значимых изменений включают:
- Улучшенные механизмы валидации: Введение новых типов валидаторов и улучшение существующих механизмов валидации, что позволяет более точно и гибко проверять данные.
- Оптимизация производительности: Значительное улучшение скорости обработки данных, особенно для больших и сложных моделей. Это позволяет использовать Pydantic в высоконагруженных приложениях и сервисах.
- Расширенные возможности конфигурации: Добавлены новые опции для настройки поведения моделей и валидаторов, что даёт разработчикам больше контроля над процессом валидации и сериализации.
- Новая документация и примеры: Обновлённая документация, включающая подробные примеры использования новых функций и возможностей. Это помогает разработчикам быстрее освоить библиотеку и начать её использовать в своих проектах.
История и развитие Pydantic — это история успеха и постоянного совершенствования. С момента своего появления библиотека прошла долгий путь, постоянно улучшаясь и адаптируясь к новым требованиям и ожиданиям разработчиков. Сегодня Pydantic является одним из самых популярных и широко используемых инструментов для валидации данных в Python, предлагая богатый набор возможностей и высокую производительность.
Основы работы с Pydantic
Базовые типы данных
Pydantic, будучи инструментом для валидации и сериализации данных, поддерживает множество базовых типов данных, что позволяет разработчикам точно определять и проверять входные данные. Эти типы данных охватывают наиболее часто используемые структуры, обеспечивая гибкость и надёжность при работе с различными данными.
1. Числовые типы
int
— Целые числа. Используется для представления целочисленных значений.float
— Числа с плавающей запятой. Используется для представления дробных значений.Decimal
— Десятичные числа высокой точности. Используется в финансовых приложениях, где важна точность вычислений.
from pydantic import BaseModel, Decimal
class Product(BaseModel):
id: int
price: float
tax: Decimal
2. Строковые типы
str
— Строки. Используется для представления текстовых данных.
class User(BaseModel):
username: str
first_name: str
last_name: str
3. Булевы типы
bool
— Логические значения (истина или ложь).
class FeatureFlag(BaseModel):
is_enabled: bool
4. Списки и кортежи
list
— Списки, содержащие элементы одного типа.tuple
— Кортежи, содержащие элементы одного или нескольких типов.
class Order(BaseModel):
items: list[str]
quantities: tuple[int, int, int]
5. Словари
dict
— Словари, содержащие пары ключ-значение.
class Config(BaseModel):
settings: dict[str, str]
6. Даты и время
datetime
— Дата и время.date
— Дата.time
— Время.timedelta
— Разница между двумя моментами времени.
from datetime import datetime, timedelta
class Event(BaseModel):
start_datetime: datetime
end_datetime: datetime
duration: timedelta
7. UUID
UUID
— Универсально уникальный идентификатор.
from uuid import UUID
class Item(BaseModel):
id: UUID
name: str
8. EmailStr и AnyUrl
EmailStr
— Валидация строк как email адресов.AnyUrl
— Валидация строк как URL.
from pydantic import EmailStr, AnyUrl
class Contact(BaseModel):
email: EmailStr
website: AnyUrl
Модели данных
Модели данных в Pydantic представляют собой основу для валидации и сериализации данных. Они позволяют разработчикам описывать структуру данных с помощью классов Python, используя аннотации типов для определения полей и их атрибутов. Это обеспечивает высокую степень типизации, автоматическую валидацию и преобразование данных, что существенно упрощает разработку приложений.
Для создания модели данных в Pydantic необходимо унаследовать класс от BaseModel
и определить поля модели с помощью аннотаций типов. Каждый атрибут модели представляет собой поле данных с указанием типа и, опционально, значения по умолчанию.
from pydantic import BaseModel, EmailStr
from datetime import datetime
from uuid import UUID
class User(BaseModel):
id: UUID
username: str
email: EmailStr
is_active: bool = True
created_at: datetime = datetime.now()
В этом примере модель
User
включает несколько полей:id
,username
,is_active
иcreated_at
. Поляis_active
иcreated_at
имеют значения по умолчанию.
Создание экземпляра модели данных происходит путём передачи соответствующих значений в конструктор модели. Pydantic автоматически выполняет валидацию и преобразование данных согласно указанным типам.
from uuid import uuid4
user = User(
id=uuid4(),
username="alice",
email="alice@example.com"
)
print(user)
Одной из ключевых особенностей Pydantic является автоматическая валидация данных при создании экземпляров моделей. Pydantic проверяет соответствие каждого переданного значения ожидаемому типу и генерирует ошибки валидации, если данные не соответствуют требованиям.
try:
user = User(
id="not-a-uuid",
username="alice",
email="alice@example.com"
)
except ValueError as e:
print(e)
В этом примере Pydantic генерирует ошибку, так как значение поля
id
не является допустимым UUID.
Pydantic также обеспечивает удобные методы для сериализации данных моделей в различные форматы, такие как JSON. Это особенно полезно при работе с веб-приложениями и API, где данные часто передаются в формате JSON.
user = User(
id=uuid4(),
username="alice",
email="alice@example.com"
)
user_json = user.model_dump_json()
print(user_json)
Метод
model_dump_json
конвертирует данные модели в строку формата JSON, готовую для передачи или хранения.
Pydantic поддерживает вложенные модели, что позволяет создавать сложные структуры данных. Вложенные модели могут использоваться в качестве типов полей, что делает возможным создание иерархий объектов.
class Address(BaseModel):
street: str
city: str
country: str
class UserWithAddress(BaseModel):
id: UUID
username: str
email: str
address: Address
address = Address(street="123 Main St", city="New York", country="USA")
user = UserWithAddress(
id=uuid4(),
username="alice",
email="alice@example.com",
address=address
)
print(user)
Использование аннотаций типов и автоматическая проверка данных делают код более надёжным и читаемым. Вложенные модели позволяют работать с комплексными структурами данных, что особенно полезно при разработке современных веб-приложений и API. Pydantic значительно упрощает работу с данными, обеспечивая высокую производительность и гибкость.
Поле модели и аннотации
Поля модели в Pydantic представляют собой ключевые элементы, которые описывают структуру данных и их типы в модели. Каждое поле определяется как атрибут класса, указывая его тип данных с помощью аннотаций типов.
Аннотации типов в Python используются для указания типов данных, которые ожидаются в полях модели. Pydantic использует эти аннотации для автоматической валидации и преобразования данных. Аннотации типов могут быть простыми (например, int
, str
) или более сложными (например, List[int]
, Dict[str, Any]
).
Пример определения полей с аннотациями типов:
from pydantic import BaseModel
from typing import List, Dict
from uuid import UUID
class Product(BaseModel):
id: UUID
name: str
price: float
tags: List[str] = []
metadata: Dict[str, str] = {}
В этом примере модель
Product
включает несколько полей:id
,name
,price
,tags
иmetadata
. Поляtags
иmetadata
имеют значения по умолчанию, что делает их необязательными при создании экземпляра модели.
Поля моделей в Pydantic могут иметь различные атрибуты, которые определяют их поведение и дополнительные свойства. Вот некоторые из наиболее часто используемых атрибутов:
1. Значения по умолчанию
Значения по умолчанию позволяют задавать стандартные значения для полей, которые не были явно указаны при создании экземпляра модели.
class User(BaseModel):
username: str
is_active: bool = True
2. Алиасы (псевдонимы)
Алиасы используются для задания альтернативных имён полей, что может быть полезно при работе с внешними API или системами, где используются другие наименования полей.
from pydantic import BaseModel, Field
class User(BaseModel):
username: str
email: str = Field(alias='user_email')
3. Валидаторы
Валидаторы позволяют задавать пользовательские правила валидации для полей. Они определяются с помощью декораторов и могут использоваться для проверки значений полей перед их сохранением в модель.
from pydantic import field_validator
class User(BaseModel):
username: str
age: int
@field_validator('age')
def check_age(cls, v):
if v < 18:
raise ValueError('Age must be at least 18')
return v
4. Конфигурационные параметры
Конфигурационные параметры позволяют настраивать поведение всей модели или отдельных полей. Они определяются в атрибуте model_config, в который передаются настройки в виде словаря.
from pydantic import ConfigDict
class User(BaseModel):
username: str
email: str
model_config = ConfigDict(
str_strip_whitespace=True,
str_min_length=1
)
Примеры использования аннотаций типов и атрибутов полей
Пример 1: Модель с различными типами полей и атрибутами
from pydantic import BaseModel, Field
from uuid import UUID
from typing import List
class Item(BaseModel):
id: UUID
name: str
description: str = None
price: float
tags: List[str] = Field(default_factory=list)
item = Item(
id="123e4567-e89b-12d3-a456-426614174000",
name="Laptop",
price=999.99
)
print(item)
Пример 2: Модель с алиасами и валидаторами
from pydantic import BaseModel, Field, field_validator
class User(BaseModel):
username: str
email: str = Field(alias='user_email')
age: int
@field_validator('age')
def check_age(cls, v):
if v < 18:
raise ValueError('Age must be at least 18')
return v
user = User(
username="alice",
user_email="alice@example.com",
age=25
)
print(user)
Поля моделей и аннотации типов в Pydantic обеспечивают гибкий механизм для описания структуры данных и их валидации. Аннотации типов позволяют точно определять ожидаемые типы данных, а атрибуты полей дают возможность задавать дополнительные параметры и правила валидации.
Валидация и обработка данных
Углубим и расширим понимание принципов и механизмов валидации данных в Pydantic.
Валидация входных данных
Валидация данных является одной из ключевых функций Pydantic, обеспечивая высокую надёжность и безопасность работы с данными. Принципы валидации в Pydantic строятся на использовании аннотаций типов и встроенных механизмов проверки данных, что позволяет автоматически проверять соответствие данных заданным требованиям.
Основные принципы валидации данных:
1. Аннотации типов: Используются для определения ожидаемых типов данных для каждого поля модели. При создании экземпляра модели, Pydantic автоматически проверяет типы переданных данных, и если они не соответствуют аннотациям, генерируются ошибки валидации.
from pydantic import BaseModel
class User(BaseModel):
id: int
name: str
age: int
# Правильные данные
user = User(id=1, name="Alice", age=30)
# Неправильные данные
try:
user = User(id="one", name="Alice", age="thirty")
except ValueError as e:
print(e)
2. Встроенные валидаторы: Pydantic предоставляет ряд встроенных валидаторов, которые автоматически проверяют данные на соответствие типам и значениям. Например, валидаторы для чисел проверяют, что значения являются числами, строки проверяются на соответствие длине и наличию недопустимых символов.
from pydantic import BaseModel, EmailStr
class User(BaseModel):
id: int
email: EmailStr
# Правильный email
user = User(id=1, email="user@example.com")
# Неправильный email
try:
user = User(id=2, email="not-an-email")
except ValueError as e:
print(e)
3. Кастомные валидаторы: Позволяют разработчикам создавать свои собственные правила валидации для данных. Это достигается с помощью декораторов @validator
, которые могут быть применены к полям модели.
from pydantic import BaseModel, field_validator
class User(BaseModel):
id: int
name: str
age: int
@field_validator('age')
def check_age(cls, v):
if v < 0:
raise ValueError('Age must be a positive integer')
return v
try:
user = User(id=1, name="Alice", age=-1)
except ValueError as e:
print(e)
Механизмы валидации данных:
1. Валидация при создании модели: Основной механизм валидации в Pydantic срабатывает при создании экземпляра модели. Все данные, переданные в конструктор модели, проверяются на соответствие аннотациям типов и правилам валидации, определённым в модели. Если данные не проходят валидацию, Pydantic генерирует подробные ошибки, указывая, какие поля не прошли проверку и по каким причинам.
2. Валидация вложенных моделей: Если модель содержит вложенные объекты, они также будут валидироваться согласно своим аннотациям типов и правилам.
from pydantic import BaseModel
class Address(BaseModel):
street: str
city: str
zip_code: str
class User(BaseModel):
id: int
name: str
address: Address
address = Address(street="Main St", city="New York", zip_code="10001")
user = User(id=1, name="Alice", address=address)
3. Валидация сложных типов данных: Pydantic поддерживает валидацию сложных типов данных, таких как списки, словари и другие коллекции. Каждый элемент коллекции валидируется согласно аннотациям типов.
from typing import List
from pydantic import BaseModel
class User(BaseModel):
id: int
name: str
friends: List[int]
user = User(id=1, name="Alice", friends=[2, 3, 4])
try:
user = User(id=1, name="Alice", friends=["two", "three"])
except ValueError as e:
print(e)
Кастомные валидаторы
Кастомные валидаторы в Pydantic создаются с помощью декоратора @field_validator
. Этот декоратор применяется к методу, который должен выполнять валидацию поля. В метод передаётся значение поля, которое необходимо проверить и, при необходимости, изменить. Если значение не проходит проверку, валидатор должен выбросить исключение ValueError
с описанием ошибки.
from pydantic import BaseModel, field_validator
class User(BaseModel):
username: str
age: int
@field_validator('age')
def check_age(cls, value):
if value < 18:
raise ValueError('Age must be at least 18')
return value
Валидаторы могут использовать контекст, чтобы принимать решения на основе значений других полей модели. Для этого используется аргумент values
, который содержит текущие значения полей модели.
from pydantic import BaseModel, field_validator
class User(BaseModel):
name: str
age: int
email: str
@field_validator('email')
def validate_email(cls, value, values):
age = values.data.get('age')
if age and age < 18:
if not value.endswith('@example.com'):
raise ValueError('Users under 18 must have an @example.com email')
return value
try:
user = User(name='Jose', age='15', email='jose@not_example.com')
except ValueError as e:
print(e)
Кастомные валидаторы позволяют создавать комплексные правила валидации, включая проверки на основе регулярных выражений, доступа к базам данных, запросов к внешним API и других сложных логик.
import re
from pydantic import BaseModel, field_validator
class User(BaseModel):
username: str
@field_validator('username')
def username_alphanumeric(cls, value):
if not re.match(r'^[a-zA-Z0-9_]+$', value):
raise ValueError('Username must be alphanumeric')
return value
try:
user = User(username='Jose@')
except ValueError as e:
print(e)
Кастомные валидаторы могут также применяться к вложенным моделям, что позволяет проверять сложные структуры данных.
from pydantic import BaseModel, field_validator
class Address(BaseModel):
street: str
city: str
class User(BaseModel):
name: str
address: Address
@field_validator('address')
def validate_address(cls, value):
if not value.street or not value.city:
raise ValueError('Both street and city must be provided')
return value
try:
user = User(name='Jose', address=Address(street='123 Main St', city=''))
except ValueError as e:
print(e)
Кастомные валидаторы в Pydantic предоставляют разработчикам инструмент для реализации сложных правил валидации данных. С их помощью можно легко создавать проверки, которые выходят за рамки стандартных встроенных валидаторов, обеспечивая высокую степень гибкости и точности валидации.
Обработка ошибок
Ошибки валидации данных — это неизбежная часть разработки любого приложения, где данные поступают из внешних источников или вводятся пользователями. Pydantic предоставляет удобные механизмы для обработки ошибок валидации, позволяя разработчикам эффективно реагировать на неправильные данные и обеспечивать их корректную обработку.
Основной тип исключений, используемый в Pydantic для обозначения ошибок валидации, — это ValidationError
. Это исключение возникает, когда данные не соответствуют определённым в модели типам или правилам валидации.
from pydantic import BaseModel, ValidationError
class User(BaseModel):
id: int
name: str
age: int
try:
user = User(id='abc', name='Alice', age='twenty-five')
except ValidationError as e:
print(e)
В этом примере
ValidationError
будет сгенерировано из-за несоответствия типов данных для полейid
иage
.
Исключение ValidationError
содержит подробную информацию о причинах ошибки, включая список ошибок, произошедших при валидации каждого поля. Это позволяет разработчикам легко определять и исправлять проблемы с данными.
Вывод будет содержать список ошибок с указанием полей, на которых произошли сбои, и сообщений о причинах ошибок:
2 validation errors for User
id
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='abc', input_type=str]
For further information visit https://errors.pydantic.dev/2.7/v/int_parsing
age
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='twenty-five', input_type=str]
For further information visit https://errors.pydantic.dev/2.7/v/int_parsing
Для обработки ошибок валидации в Pydantic можно использовать стандартные конструкции обработки исключений Python. Это позволяет логировать ошибки, показывать пользователям информативные сообщения или выполнять другие действия в зависимости от потребностей приложения.
import logging
from pydantic import BaseModel, ValidationError
# Настройка логирования
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class User(BaseModel):
id: int
name: str
age: int
try:
user = User(id='abc', name='Alice', age='twenty-five')
except ValidationError as e:
logger.error("Validation error: %s", e)
# Дополнительные действия по обработке ошибки
Помимо использования стандартного ValidationError
, можно создавать пользовательские исключения для более специфической обработки ошибок валидации. Это может быть полезно в случаях, когда требуется особая логика обработки для разных типов ошибок.
from pydantic import BaseModel, field_validator
class CustomValidationError(Exception):
def __init__(self, message: str):
super().__init__(message)
class User(BaseModel):
id: int
name: str
age: int
@field_validator('age')
def check_age(cls, value):
if value < 0:
raise CustomValidationError('Age must be a positive integer')
return value
try:
user = User(id=1, name='Alice', age=-5)
except CustomValidationError as e:
print(e)
# Вывод: Age must be a positive integer
Обработка ошибок валидации в Pydantic — это важный аспект разработки, который позволяет обеспечить корректную работу с данными и повысить надёжность приложений. Использование исключений ValidationError
, создание пользовательских валидаторов и логирование ошибок помогают разработчикам эффективно управлять ошибками и обеспечивать высокое качество данных.
Продвинутые возможности
Подробнее о вложенности моделей
Pydantic обеспечивает разработчикам возможность работать с вложенными моделями, что позволяет создавать и валидировать сложные структуры данных. Вложенные модели полезны в случаях, когда данные имеют иерархическую природу или содержат несколько уровней вложенности. Использование вложенных моделей не только упрощает работу с данными, но и повышает читаемость и поддерживаемость кода.
Вложенные модели в Pydantic определяются аналогично обычным моделям, но используются в качестве типов для полей других моделей. Это позволяет создавать сложные, многослойные структуры данных, где каждая вложенная модель имеет свои собственные правила валидации и атрибуты.
from pydantic import BaseModel
class Address(BaseModel):
street: str
city: str
zip_code: str
class User(BaseModel):
id: int
name: str
address: Address
При создании экземпляров моделей, содержащих вложенные модели, Pydantic автоматически создаёт экземпляры вложенных моделей и валидирует их данные. Это позволяет легко работать с вложенными структурами, не беспокоясь о валидации каждого уровня вложенности вручную.
address_data = {
"street": "123 Main St",
"city": "New York",
"zip_code": "10001"
}
user_data = {
"id": 1,
"name": "Alice",
"address": address_data
}
user = User(**user_data)
print(user)
Pydantic выполняет валидацию данных на всех уровнях вложенности. Это означает, что если вложенная модель содержит ошибки валидации, они будут автоматически обнаружены и обработаны. Ошибки валидации во вложенных моделях включаются в общую структуру ошибок ValidationError
.
from pydantic import BaseModel, ValidationError
class Address(BaseModel):
street: str
city: str
zip_code: str
class User(BaseModel):
id: int
name: str
address: Address
invalid_user_data = {
"id": 1,
"name": "Alice",
"address": {
"street": "123 Main St",
"city": "New York",
"zip_code": 10001 # zip_code должен быть строкой
}
}
try:
user = User(**invalid_user_data)
except ValidationError as e:
print(e.json())
Преимущества использования вложенных моделей
- Повышенная читаемость и поддерживаемость кода
Вложенные модели помогают разделить сложные структуры данных на логически обоснованные части, что упрощает чтение и поддержку кода. - Автоматическая валидация на всех уровнях
Pydantic автоматически валидирует данные на всех уровнях вложенности, что снижает вероятность ошибок и обеспечивает целостность данных. - Повторное использование моделей
Вложенные модели можно повторно использовать в различных частях приложения, что способствует уменьшению дублирования кода и упрощает обновление моделей.
Примеры использования вложенных моделей:
Пример 1: Модель заказа с вложенными моделями для товаров и адреса доставки
from pydantic import BaseModel
from typing import List
class Item(BaseModel):
name: str
price: float
class Address(BaseModel):
street: str
city: str
zip_code: str
class Order(BaseModel):
order_id: int
items: List[Item]
shipping_address: Address
order_data = {
"order_id": 123,
"items": [
{"name": "Laptop", "price": 999.99},
{"name": "Mouse", "price": 25.75}
],
"shipping_address": {
"street": "456 Elm St",
"city": "Los Angeles",
"zip_code": "90001"
}
}
order = Order(**order_data)
print(order)
Пример 2: Модель компании с вложенными моделями для сотрудников и адреса офиса
from pydantic import BaseModel
from typing import List
class Address(BaseModel):
street: str
city: str
zip_code: str
class Employee(BaseModel):
employee_id: int
name: str
position: str
class Company(BaseModel):
name: str
address: Address
employees: List[Employee]
company_data = {
"name": "Tech Corp",
"address": {
"street": "789 Maple Ave",
"city": "San Francisco",
"zip_code": "94107"
},
"employees": [
{"employee_id": 1, "name": "John Doe", "position": "CEO"},
{"employee_id": 2, "name": "Jane Smith", "position": "CTO"}
]
}
company = Company(**company_data)
print(company)
Динамическое создание моделей
В некоторых случаях при разработке приложений требуется динамическое создание моделей данных на основе входных параметров или конфигураций, которые известны только во время выполнения программы. Pydantic предоставляет инструмент для таких ситуаций — функцию create_model
, которая позволяет создавать модели на лету, гибко определяя их структуру и поведение.
Функция create_model
позволяет динамически создавать новые модели Pydantic, определяя их поля и атрибуты во время выполнения. Это особенно полезно, когда структура данных неизвестна заранее или может изменяться в зависимости от условий выполнения программы.
from pydantic import BaseModel, create_model
# Динамическое создание модели
DynamicModel = create_model(
'DynamicModel', # Имя модели
name=(str, ...), # Поле name с типом str и обязательное
age=(int, 30) # Поле age с типом int и значением по умолчанию 30
)
# Создание экземпляра динамической модели
dynamic_instance = DynamicModel(name='Alice')
print(dynamic_instance)
Функция create_model
предоставляет широкие возможности для создания динамических моделей.
- Определение полей: Поля модели задаются в виде именованных аргументов функции, где ключом является имя поля, а значением — кортеж, состоящий из типа поля и его значения по умолчанию. Если значение по умолчанию не указано, поле считается обязательным.
- Использование валидаторов и настроек: Для создания динамической модели можно определить валидаторы, используя декораторы
@field_validator
. Настройки модели можно передать через класс конфигурации, который наследуется от созданной модели. - Наследование от существующих моделей:
create_model
позволяет создавать новые модели на основе существующих, добавляя или изменяя поля и атрибуты. При этом можно добавлять валидаторы и настройки конфигурации через наследование.
from pydantic import BaseModel, create_model, ValidationError, field_validator
# Создание базовой модели
class BaseUser(BaseModel):
id: int
# Динамическое создание модели
DynamicUser = create_model(
'DynamicUser',
__base__=BaseUser,
name=(str, ...),
age=(int, ...)
)
# Добавление валидатора
class DynamicUserWithValidator(DynamicUser):
@field_validator('age')
def check_age(cls, v):
if v < 18:
raise ValueError('Age must be at least 18')
return v
# Альтернативный способ добавления конфигурации через наследование
class ConfiguredUser(DynamicUserWithValidator):
class Config:
str_min_length = 1
# Создание экземпляра динамической модели
try:
dynamic_user = ConfiguredUser(id=1, name='Bob', age=17)
except ValidationError as e:
print(e)
Динамическое создание моделей особенно полезно в случаях, когда структура данных определяется на основе внешних конфигураций, таких как JSON-схемы, базы данных или пользовательские вводы.
import json
from pydantic import create_model
# Пример JSON-схемы
json_schema = '''
{
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"email": {"type": "string"}
},
"required": ["name", "email"]
}
'''
# Парсинг JSON-схемы
schema = json.loads(json_schema)
# Создание модели на основе JSON-схемы
fields = {
key: (str if value['type'] == 'string' else int, ...)
for key, value in schema['properties'].items()
}
DynamicModelFromJSON = create_model('DynamicModelFromJSON', **fields)
# Создание экземпляра динамической модели
instance = DynamicModelFromJSON(name='Alice', email='alice@example.com', age=25)
print(instance)
Преимущества использования create_model
:
- Гибкость: Возможность динамически создавать модели позволяет адаптироваться к изменениям в данных и конфигурациях без необходимости заранее определять все возможные структуры.
- Удобство:
create_model
упрощает создание моделей для временных или одноразовых структур данных, что особенно полезно в сценариях тестирования и быстрого прототипирования. - Интеграция с существующим кодом: Динамически созданные модели могут наследовать от существующих моделей и использовать их валидаторы и настройки, что позволяет легко интегрировать их в существующий код и инфраструктуру.
Валидация сложных типов данных
Валидация списков: Списки являются одной из самых распространённых структур данных, и Pydantic обеспечивает простые и гибкие способы для их валидации. Вы можете определить список, указав его тип с помощью аннотации типов List
из модуля typing
.
class User(BaseModel):
id: int
name: str
tags: list[str]
user = User(id=1, name='Alice', tags=['admin', 'user'])
print(user)
Если данные не соответствуют указанному типу, Pydantic сгенерирует ошибку валидации.
try:
user = User(id=1, name='Alice', tags='not-a-list')
except ValidationError as e:
print(e
Валидация словарей: Словари используются для хранения данных в формате ключ-значение, и Pydantic поддерживает их валидацию с помощью аннотации типов Dict
из модуля typing
.
class Config(BaseModel):
settings: dict[str, str]
config = Config(settings={"theme": "dark", "language": "en"})
print(config)
Если данные не соответствуют ожидаемой структуре словаря, Pydantic сгенерирует ошибку.
try:
config = Config(settings="not-a-dict")
except ValidationError as e:
print(e)
Валидация собственных типов: Pydantic также поддерживает создание и валидацию собственных типов данных. Это может быть полезно, когда необходимо определить специфические правила валидации или преобразования данных.
from pydantic import BaseModel, conint
class PositiveInt(BaseModel):
value: conint(gt=0)
positive_value = PositiveInt(value=10)
print(positive_value)
try:
negative_value = PositiveInt(value=-10)
except ValueError as e:
print(e)
Вложенные сложные типы: Pydantic позволяет создавать сложные структуры данных, объединяя списки, словари и собственные типы. Это особенно полезно для валидации и обработки сложных и многослойных данных.
from pydantic import BaseModel
from typing import List, Dict
class Address(BaseModel):
street: str
city: str
class User(BaseModel):
id: int
name: str
addresses: List[Address]
preferences: Dict[str, str]
user_data = {
"id": 1,
"name": "Alice",
"addresses": [
{"street": "123 Main St", "city": "New York"},
{"street": "456 Elm St", "city": "Los Angeles"}
],
"preferences": {"theme": "dark", "language": "en"}
}
user = User(**user_data)
print(user)
Интеграция с другими библиотеками
FastAPI
FastAPI — это современный веб-фреймворк для создания API на Python, который выделяется своей скоростью, простотой использования и поддержкой асинхронных операций. Одной из ключевых особенностей FastAPI является тесная интеграция с Pydantic, которая позволяет автоматически валидировать и сериализовать данные, поступающие в API. Это значительно упрощает разработку надёжных и безопасных веб-приложений.
FastAPI использует модели Pydantic для определения схем запросов и ответов. Это позволяет автоматизировать процесс валидации данных и улучшить качество кода за счёт использования типизированных моделей.
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
class User(BaseModel):
id: int
name: str
email: EmailStr
@app.post("/users/")
async def create_user(user: User):
return user
В этом примере создаётся API, которое принимает данные пользователя в формате JSON, валидирует их с помощью модели Pydantic и возвращает данные обратно.
Одной из сильных сторон интеграции Pydantic с FastAPI является автоматическая валидация данных. FastAPI автоматически проверяет данные запросов на соответствие моделям Pydantic и возвращает понятные сообщения об ошибках, если данные не проходят валидацию.
{
"id": "not-an-integer",
"name": "Alice",
"email": "alice@example.com"
}
FastAPI автоматически вернёт сообщение об ошибке:
{
"detail": [
{
"type": "int_parsing",
"loc": [
"body",
"id"
],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "not-an-integer"
}
]
}
FastAPI и Pydantic поддерживают работу с вложенными моделями и сложными структурами данных, что позволяет создавать API для обработки комплексных данных.
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
from typing import List
app = FastAPI()
class Address(BaseModel):
street: str
city: str
class User(BaseModel):
id: int
name: str
email: EmailStr
addresses: List[Address]
@app.post("/users/")
async def create_user(user: User):
return user
FastAPI автоматически генерирует документацию для API, включая схемы запросов и ответов на основе моделей Pydantic. Это делает API самодокументируемым и упрощает интеграцию с другими системами.
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
class User(BaseModel):
id: int
name: str
email: str
users = {}
@app.post("/users/", response_model=User)
async def create_user(user: User):
if user.id in users:
raise HTTPException(status_code=400, detail="User already exists")
users[user.id] = user
return user
@app.get("/users/{user_id}", response_model=User)
async def get_user(user_id: int):
if user_id not in users:
raise HTTPException(status_code=404, detail="User not found")
return users[user_id]
FastAPI поддерживает асинхронные операции, что позволяет создавать высокопроизводительные и масштабируемые API. Интеграция с Pydantic остаётся такой же простой и эффективной даже при использовании асинхронного кода.
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
@app.post("/items/")
async def create_item(item: Item):
# Асинхронная обработка данных
await some_async_function(item)
return item
FastAPI и Pydantic используются в различных реальных проектах для создания надёжных и масштабируемых веб-сервисов. Примеры таких проектов включают RESTful API для e-commerce платформ, системы управления пользователями, и интеграционные шлюзы для микросервисов.
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List
app = FastAPI()
class Item(BaseModel):
id: int
name: str
price: float
items = {}
@app.post("/items/", response_model=Item)
async def create_item(item: Item):
if item.id in items:
raise HTTPException(status_code=400, detail="Item already exists")
items[item.id] = item
return item
@app.get("/items/", response_model=List[Item])
async def list_items():
return list(items.values())
@app.get("/items/{item_id}", response_model=Item)
async def get_item(item_id: int):
if item_id not in items:
raise HTTPException(status_code=404, detail="Item not found")
return items[item_id]
SQLAlchemy
Основная идея интеграции Pydantic с SQLAlchemy заключается в использовании моделей Pydantic для валидации данных, которые сохраняются в базу данных или извлекаются из неё. Это позволяет объединить преимущества обеих библиотек: управление данными с помощью SQLAlchemy и строгую валидацию данных с помощью Pydantic.
Сначала необходимо определить модели SQLAlchemy, которые будут использоваться для представления таблиц базы данных. В качестве примера создадим модель пользователя:
from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True, index=True)
name = Column(String, index=True)
email = Column(String, unique=True, index=True)
# Настройка подключения к базе данных
DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# Создание таблиц
Base.metadata.create_all(bind=engine)
После определения моделей SQLAlchemy необходимо создать соответствующие модели Pydantic для валидации данных. Модели Pydantic будут использоваться для приёма данных от клиентов и их валидации перед сохранением в базу данных.
from pydantic import BaseModel, EmailStr
class UserCreate(BaseModel):
name: str
email: EmailStr
class UserRead(BaseModel):
id: int
name: str
email: EmailStr
class Config:
from_attributes = True
Модель UserCreate
будет использоваться для валидации данных при создании нового пользователя, а модель UserRead
— для сериализации данных, извлечённых из базы данных.
Рассмотрим, как использовать Pydantic и SQLAlchemy для создания и получения данных в API. Создадим функцию для обработки создания пользователя, которая будет принимать данные, валидировать их с помощью модели Pydantic и сохранять в базу данных с использованием SQLAlchemy.
from fastapi import FastAPI, HTTPException, Depends
from sqlalchemy.orm import Session
app = FastAPI()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.post("/users/", response_model=UserRead)
def create_user(user: UserCreate, db: Session = Depends(get_db)):
db_user = db.query(User).filter(User.email == user.email).first()
if db_user:
raise HTTPException(status_code=400, detail="Email already registered")
new_user = User(name=user.name, email=user.email)
db.add(new_user)
db.commit()
db.refresh(new_user)
return new_user
Создадим функцию для получения данных пользователя из базы данных и их сериализации с помощью модели Pydantic.
@app.get("/users/{user_id}", response_model=UserRead)
def read_user(user_id: int, db: Session = Depends(get_db)):
user = db.query(User).filter(User.id == user_id).first()
if user is None:
raise HTTPException(status_code=404, detail="User not found")
return user
Добавим функции для обновления и удаления пользователей с валидацией данных.
class UserUpdate(BaseModel):
name: str
email: EmailStr
@app.put("/users/{user_id}", response_model=UserRead)
def update_user(user_id: int, user_update: UserUpdate, db: Session = Depends(get_db)):
user = db.query(User).filter(User.id == user_id).first()
if user is None:
raise HTTPException(status_code=404, detail="User not found")
user.name = user_update.name
user.email = user_update.email
db.commit()
db.refresh(user)
return user
@app.delete("/users/{user_id}", response_model=UserRead)
def delete_user(user_id: int, db: Session = Depends(get_db)):
user = db.query(User).filter(User.id == user_id).first()
if user is None:
raise HTTPException(status_code=404, detail="User not found")
db.delete(user)
db.commit()
return user
Преимущества интеграции Pydantic и SQLAlchemy
- Строгая валидация данных
Использование Pydantic обеспечивает строгую валидацию данных перед их сохранением в базу данных, что уменьшает вероятность ошибок и улучшает целостность данных. - Удобство работы с данными
Модели Pydantic позволяют легко сериализовать и десериализовать данные, упрощая работу с JSON и другими форматами. - Повышенная читаемость и поддерживаемость кода
Разделение моделей данных на валидационные (Pydantic) и ORM (SQLAlchemy) помогает поддерживать чистоту и структурированность кода.
Django
Интеграция Pydantic с Django включает использование моделей Pydantic для валидации данных и сериализации, что позволяет повысить надёжность и читаемость кода. Основные шаги включают определение моделей Pydantic и использование их для валидации данных в Django представлениях и других компонентах.
Первый шаг в интеграции — создание моделей Pydantic, которые будут использоваться для валидации данных и сериализации. Рассмотрим пример модели пользователя:
from pydantic import BaseModel, EmailStr
class UserCreate(BaseModel):
username: str
email: EmailStr
password: str
class UserRead(BaseModel):
id: int
username: str
email: EmailStr
class Config:
from_attributes = True
Модели Pydantic могут использоваться в Django представлениях для валидации данных запросов. Рассмотрим пример представления для создания пользователя:
from django.http import JsonResponse
from django.views import View
from pydantic import ValidationError
from .models import User as DjangoUser
from .pydantic_models import UserCreate, UserRead
class CreateUserView(View):
def post(self, request, *args, **kwargs):
try:
data = UserCreate.model_validate_json(request.body)
except ValidationError as e:
return JsonResponse(e.errors(), status=400, safe=False)
user = DjangoUser.objects.create(
username=data.username,
email=data.email,
password=data.password # Здесь следует использовать хэширование паролей
)
user_data = UserRead.model_validate(user)
return JsonResponse(user_data.model_dump())
В этом примере данные запроса валидируются с использованием модели
UserCreate
, а затем создаётся экземпляр модели DjangoUser
. Для ответа используется модельUserRead
, которая преобразует данные в формат JSON.
Хотя Django предлагает собственные формы для валидации данных, Pydantic может быть использован для дополнительной проверки и преобразования данных. Рассмотрим пример формы, которая использует Pydantic для валидации:
from django import forms
from pydantic import BaseModel, EmailStr, ValidationError
class UserForm(forms.Form):
username = forms.CharField(max_length=100)
email = forms.EmailField()
password = forms.CharField(widget=forms.PasswordInput)
def clean(self):
cleaned_data = super().clean()
try:
UserCreate(**cleaned_data)
except ValidationError as e:
raise forms.ValidationError(e.errors())
class UserCreate(BaseModel):
username: str
email: EmailStr
password: str
В этом примере форма Django использует модель Pydantic
UserCreate
для валидации данных в методеclean
.
Pydantic также может быть использован для сериализации данных, например, при возвращении JSON-ответов из представлений Django. Рассмотрим пример:
from django.http import JsonResponse
from django.views import View
from .models import User as DjangoUser
from .pydantic_models import UserRead
class ListUsersView(View):
def get(self, request, *args, **kwargs):
users = DjangoUser.objects.all()
users_data = [UserRead.model_validate(user) for user in users]
return JsonResponse([user.model_dump() for user in users_data], safe=False)
Преимущества использования Pydantic с Django
- Строгая валидация данных
Использование Pydantic для валидации данных позволяет обеспечить более строгий контроль за входными данными, что снижает вероятность ошибок и повышает надёжность приложений. - Удобство и ясность кода
Модели Pydantic упрощают определение и использование структур данных, улучшая читаемость и поддерживаемость кода. - Повторное использование моделей
Модели Pydantic можно использовать повторно в различных частях приложения, включая представления, формы и сериализаторы, что снижает дублирование кода и упрощает его обновление.