Введение в Pydantic: Основы и продвинутые возможности
Введение
Что такое Pydantic?
В современном программировании, особенно в разработке на Python, важно эффективно управлять данными. От качества обработки данных зависит стабильность и безопасность приложений. Здесь на сцену выходит Pydantic - библиотека, которая предлагает довольно простой способ валидации и сериализации данных.
Pydantic создан для того, чтобы облегчить разработчикам работу с данными, делая её более предсказуемой и безопасной. Основная цель библиотеки - обеспечить строгую проверку типов данных и их валидацию с минимальными усилиями со стороны программиста. Pydantic использует возможности аннотаций типов Python для описания моделей данных и автоматически генерирует валидаторы, которые проверяют данные на соответствие этим аннотациям.
Основные возможности Pydantic:
1. Модели данных: Pydantic предоставляет удобный способ описания моделей данных через классы Python. Каждая модель представляет собой набор полей с определёнными типами данных и возможными значениями по умолчанию. Это позволяет явно описывать структуру данных и требования к ним.
from pydantic import BaseModel
class User(BaseModel):
id: int
name: str
age: int = 182. Валидация данных: Одна из ключевых возможностей 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 и другими форматами данных, где часто встречаются вложенные объекты.
from pydantic import BaseModel
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 BaseModel, 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: Decimal2. Строковые типы
str- Строки. Используется для представления текстовых данных.
from pydantic import BaseModel
class User(BaseModel):
username: str
first_name: str
last_name: str3. Булевы типы
bool- Логические значения (истина или ложь).
from pydantic import BaseModel
class FeatureFlag(BaseModel):
is_enabled: bool4. Списки и кортежи
list- Списки, содержащие элементы одного типа.tuple- Кортежи, содержащие элементы одного или нескольких типов.
from pydantic import BaseModel
class Order(BaseModel):
items: list[str]
quantities: tuple[int, int, int]5. Словари
dict- Словари, содержащие пары ключ-значение.
from pydantic import BaseModel
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 pydantic import BaseModel
from uuid import UUID
class Item(BaseModel):
id: UUID
name: str8. EmailStr и AnyUrl
EmailStr- Валидация строк как email адресов.AnyUrl- Валидация строк как URL.
from pydantic import BaseModel, 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 = True2. Алиасы (псевдонимы)
Алиасы используются для задания альтернативных имён полей, что может быть полезно при работе с внешними 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 v4. Конфигурационные параметры
Конфигурационные параметры позволяют настраивать поведение всей модели или отдельных полей. Они определяются в атрибуте 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 userFastAPI автоматически генерирует документацию для 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 itemFastAPI и 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 можно использовать повторно в различных частях приложения, включая представления, формы и сериализаторы, что снижает дублирование кода и упрощает его обновление.