PydanticMiddleTechnical
Как Pydantic обрабатывает типы datetime, UUID и HttpUrl из коробки?
Pydantic v2 из коробки парсит datetime (ISO 8601, unix timestamp), UUID (строки всех форматов) и HttpUrl (структурированный объект с валидацией схемы). Каждый тип имеет нюансы coercion и сериализации.
Встроенные типы Pydantic v2
Pydantic v2 поддерживает богатый набор стандартных Python-типов с автоматической валидацией и coercion. Три наиболее важных в backend-разработке: datetime, UUID и HttpUrl.
datetime: парсинг и timezone
from datetime import datetime
from pydantic import BaseModel
class Event(BaseModel):
created_at: datetime
scheduled_at: datetime
# Pydantic принимает ISO 8601 строку, unix timestamp (int/float), объект datetime
e = Event(
created_at="2024-01-15T10:30:00Z",
scheduled_at=1705312200, # unix timestamp
)
print(e.created_at) # 2024-01-15 10:30:00+00:00
print(e.scheduled_at) # datetime объект
# Что не принимает:
# Event(created_at="15 января 2024") -> ValidationError
# Event(created_at="01/15/2024") -> ValidationError (не ISO 8601)
Важно: Pydantic v2 по умолчанию принимает datetime без timezone (naive). Чтобы требовать timezone-aware datetime, используйте datetime с AwareDatetime из pydantic:
from pydantic import AwareDatetime, BaseModel
class StrictEvent(BaseModel):
created_at: AwareDatetime # ValidationError если нет timezone
UUID: все форматы строк
from uuid import UUID
from pydantic import BaseModel
class Resource(BaseModel):
id: UUID
# Принимает любой стандартный формат
r1 = Resource(id="550e8400-e29b-41d4-a716-446655440000") # с дефисами
r2 = Resource(id="550e8400e29b41d4a716446655440000") # без дефисов
r3 = Resource(id="{550e8400-e29b-41d4-a716-446655440000}") # с фигурными скобками
# Объект UUID тоже принимается
from uuid import uuid4
r4 = Resource(id=uuid4())
print(type(r1.id)) # <class 'uuid.UUID'>
print(r1.id) # UUID('550e8400-e29b-41d4-a716-446655440000')
# Сериализация: по умолчанию в строку
print(r1.model_dump()) # {'id': UUID('...')}
print(r1.model_dump(mode='json')) # {'id': '550e8400-...'}
HttpUrl: структурированная валидация URL
from pydantic import BaseModel, HttpUrl
class Webhook(BaseModel):
callback_url: HttpUrl
w = Webhook(callback_url="https://example.com/hook?token=abc")
print(w.callback_url) # https://example.com/hook?token=abc
print(w.callback_url.host) # example.com
print(w.callback_url.scheme) # https
print(w.callback_url.path) # /hook
print(w.callback_url.query) # token=abc
# Что не принимает:
# Webhook(callback_url="ftp://example.com") -> ValidationError (не http/https)
# Webhook(callback_url="not-a-url") -> ValidationError
# Webhook(callback_url="http://") -> ValidationError (нет хоста)
В Pydantic v2 HttpUrl — не строка, а объект типа Url. При сериализации через model_dump() возвращает объект Url, при model_dump(mode='json') — строку. Это частая причина ошибок при сравнении с ==.
Сериализация и совместимость
from datetime import datetime, timezone
from uuid import UUID
from pydantic import BaseModel, HttpUrl
class Entity(BaseModel):
id: UUID
created_at: datetime
source: HttpUrl
e = Entity(
id="550e8400-e29b-41d4-a716-446655440000",
created_at="2024-01-15T10:30:00Z",
source="https://api.example.com/v1",
)
# Python-объекты
d = e.model_dump()
print(type(d["id"])) # <class 'uuid.UUID'>
print(type(d["created_at"])) # <class 'datetime.datetime'>
print(type(d["source"])) # <class 'pydantic_core._pydantic_core.Url'>
# JSON-совместимые типы
d_json = e.model_dump(mode="json")
print(type(d_json["id"])) # <class 'str'>
print(type(d_json["created_at"])) # <class 'str'>
print(type(d_json["source"])) # <class 'str'>
Подводные камни
HttpUrlв Pydantic v2 — объект, а не строка. Кодif url == "https://example.com"вернётFalse— нужноstr(url) == "https://example.com".datetimeбез timezone (naive) принимается по умолчанию — если бизнес-логика требует timezone, используйтеAwareDatetime, иначе данные из разных источников несравнимы.- Unix timestamp как
intпринимается дляdatetime, но в Pydantic v2 strict-режим (model_config = {"strict": True}) запрещает coercion из int — передавайте только строки ISO 8601. model_dump()возвращаетUUID-объект, а не строку — передача результата напрямую вjson.dumps()вызоветTypeError. Используйтеmodel_dump_json()илиmodel_dump(mode="json").HttpUrlнормализует URL: добавляет trailing slash к корневым URL (https://example.com→https://example.com/) — это может ломать сравнения и тесты.- Для
AnyUrl(в отличие отHttpUrl) схема не ограничена http/https — не используйте его там, где нужна строгая валидация. - При десериализации из БД (SQLAlchemy)
datetime-объекты без timezone передаются как naive — при совместном использовании сAwareDatetimeполучитеValidationError. - В JSON Schema
HttpUrlгенерирует{"type": "string", "format": "uri"}— OpenAPI-клиенты не знают про ограничение http/https, документация может вводить в заблуждение.
Common mistakes
- Описывать datetime uuid httpurl только как термин и не показывать механизм на минимальном примере.
- Игнорировать ошибки, пустые данные, конкурентный доступ или границы транзакции.
- Не связывать поведение с официальным контрактом Pydantic и реальной эксплуатацией.
What the interviewer is testing
- Объясняет datetime uuid httpurl через последовательность действий, а не через набор ключевых слов.
- Приводит короткий кодовый пример или production-сценарий с ожидаемым поведением.
- Называет хотя бы один риск: производительность, безопасность, транзакции, память или сопровождение.