⚖️ Старый vs Новый: Pydantic v1 + SQLAlchemy 1.x (лекция) vs v2 + 2.x
⚡ Кратко: что изменилось
- Pydantic:
@validator→@field_validator+@classmethod;constr/condecimal→Annotated[..., Field(...)];class Config→model_config = ConfigDict(...) - SQLAlchemy:
declarative_base()→class Base(DeclarativeBase);Column(Integer,...)→Mapped[int] = mapped_column(...);sessionmaker(bind=engine)→with Session(engine) as session: - Лекционный код в левой колонке — ошибок нет, он работал в старых версиях. Теперь правая колонка — правильный выбор.
⚠️ Внимание: код в левой колонке — это синтаксис лекции (Pydantic v1 / SQLAlchemy 1.x). Именно так написан учебный материал. Правая колонка показывает тот же код на современном API. В реальных проектах используйте правую колонку.
Pydantic: v1 → v2
1. Кастомный валидатор: @validator → @field_validator
🔥 Из лекции (Pydantic v1)
# Задание 4 — из лекции (Pydantic v1)
from pydantic import BaseModel, validator
from datetime import datetime, timedelta
class Appointment(BaseModel):
patient_name: str
appointment_date: datetime
@validator('appointment_date')
def check_appointment_date(cls, v):
if v < datetime.now() + timedelta(days=1):
raise ValueError("Appointment must be scheduled at least 24 hours in advance.")
return v
- ❌
@validator— устарел в Pydantic v2, вызывает DeprecationWarning - ❌ Нет
@classmethod— в v2 обязателен, в v1 необязателен - ❌ Нет аннотаций типов для аргументов и возврата
✨ Современно (Pydantic v2)
# Тот же валидатор на Pydantic v2
from pydantic import BaseModel, field_validator
from datetime import datetime, timedelta
class Appointment(BaseModel):
patient_name: str
appointment_date: datetime
@field_validator('appointment_date')
@classmethod
def check_appointment_date(cls, v: datetime) -> datetime:
if v < datetime.now() + timedelta(hours=24):
raise ValueError(
"Appointment must be scheduled at least 24 hours in advance."
)
return v
- ✅
@field_validator— актуальный декоратор Pydantic v2 - ✅
@classmethod— обязательный в v2 - ✅ Аннотации типов для аргумента и возврата
2. constr/condecimal → Annotated + Field
🔥 Из лекции (Pydantic v1)
# Задание 3 — из лекции (Pydantic v1)
from pydantic import BaseModel, constr, condecimal
class Transaction(BaseModel):
amount: condecimal(gt=0)
transaction_type: constr(regex="^(debit|credit)$")
currency: constr(min_length=3, max_length=3)
class Config:
anystr_strip_whitespace = True
- ❌
constr,condecimal— устаревшие функции Pydantic v1 - ❌
regex=→ в v2 переименован вpattern= - ❌
class Config— устарел в v2 - ❌
anystr_strip_whitespace— устарело в v2
✨ Современно (Pydantic v2)
# Тот же класс на Pydantic v2
from pydantic import BaseModel, Field, ConfigDict
from typing import Annotated
from decimal import Decimal
class Transaction(BaseModel):
amount: Annotated[Decimal, Field(gt=0)]
transaction_type: Annotated[str, Field(pattern=r"^(debit|credit)$")]
currency: Annotated[str, Field(min_length=3, max_length=3)]
model_config = ConfigDict(str_strip_whitespace=True)
- ✅
Annotated[..., Field(...)]— стандартный Python-подход - ✅
pattern=вместоregex= - ✅
model_config = ConfigDict(...)вместоclass Config - ✅
str_strip_whitespace=Trueв ConfigDict
3. class Config: schema_extra → model_config = ConfigDict(json_schema_extra)
🔥 Из лекции (Pydantic v1)
# Задание 2 — из лекции (Pydantic v1)
from pydantic import BaseModel, EmailStr, Field
class UserProfile(BaseModel):
username: str
password: str = Field(..., min_length=8,
description="Password must be at least 8 characters long")
email: EmailStr
class Config:
schema_extra = {
"example": {
"username": "john_doe",
"password": "securePassword123",
"email": "john.doe@example.com"
}
}
- ❌
class Config— устарел в Pydantic v2 - ❌
schema_extra→ переименован вjson_schema_extra - ❌
Field(..., ...)— позиционный...для обязательного поля нужен только в v1
✨ Современно (Pydantic v2)
# Тот же класс на Pydantic v2
from pydantic import BaseModel, EmailStr, Field, ConfigDict
class UserProfile(BaseModel):
username: str
password: str = Field(
min_length=8,
description="Password must be at least 8 characters long"
)
email: EmailStr
model_config = ConfigDict(
json_schema_extra={
"example": {
"username": "john_doe",
"password": "securePassword123",
"email": "john.doe@example.com"
}
}
)
- ✅
model_config = ConfigDict(...) - ✅
json_schema_extra— актуальное название - ✅
Field(min_length=8)— без лишнего позиционного...
SQLAlchemy: 1.x → 2.x
4. declarative_base() → class Base(DeclarativeBase)
🔥 Из лекции (SQLAlchemy 1.x)
# Задание 3 SQLAlchemy — из лекции (1.x)
from sqlalchemy.orm import declarative_base
from sqlalchemy import Column, Integer, String
Base = declarative_base() # функция, не класс
- ❌
declarative_base()— устарело в 2.x (перемещено в legacy)
✨ Современно (SQLAlchemy 2.x)
# Базовый класс на SQLAlchemy 2.x
from sqlalchemy.orm import DeclarativeBase
class Base(DeclarativeBase):
pass # наследование от класса
- ✅ Наследование — Python-идиоматичный подход
- ✅ Поддерживает статические аннотации типов
5. Column(Integer, ...) → Mapped[int] = mapped_column(...)
🔥 Из лекции (SQLAlchemy 1.x)
# Модель User из лекции (1.x)
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True) # без аннотаций
name = Column(String(50))
age = Column(Integer)
- ❌ Нет аннотаций типов — IDE не знает, что
user.nameэто строка - ❌ Mypy/Pyright не проверяют типы
✨ Современно (SQLAlchemy 2.x)
# Та же модель User на 2.x
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy import String, Integer
class User(Base):
__tablename__ = 'users'
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(String(50))
age: Mapped[int] = mapped_column(Integer)
- ✅ Аннотации типов — IDE знает точный тип каждого поля
- ✅ Работает с Mypy, Pyright, Pylance
- ✅
Mapped[тип | None]явно показывает необязательные поля
6. sessionmaker(bind=engine) → with Session(engine) as session:
🔥 Из лекции (SQLAlchemy 1.x)
# Задание 6 SQLAlchemy — из лекции (1.x)
from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind=engine) # bind= устарело
session = Session()
session.add(new_user)
session.commit()
session.delete(new_user)
session.commit()
session.close() # нужно закрывать вручную!
- ❌
bind=engine— устарело в SQLAlchemy 2.x - ❌ Ручное
session.close()— легко забыть
✨ Современно (SQLAlchemy 2.x)
# Контекстный менеджер — закрытие автоматическое
from sqlalchemy.orm import Session
with Session(engine) as session:
session.add(new_user)
session.commit()
session.delete(new_user)
session.commit()
# Сессия закрывается автоматически при выходе из with-блока
- ✅
with Session(engine)— контекстный менеджер, без риска забыть close() - ✅ Не нужен
bind=
Итоговая таблица отличий
| Возможность | Лекция (старое) | Современное |
|---|---|---|
| Кастомный валидатор Pydantic | @validator |
@field_validator + @classmethod |
| Ограничения типа Decimal | condecimal(gt=0) |
Annotated[Decimal, Field(gt=0)] |
| Ограничения строки (regex) | constr(regex=...) |
Annotated[str, Field(pattern=...)] |
| Конфигурация модели Pydantic | class Config: schema_extra / anystr_strip_whitespace |
model_config = ConfigDict(json_schema_extra=, str_strip_whitespace=) |
| Базовый класс SQLAlchemy | declarative_base() |
class Base(DeclarativeBase) |
| Поля модели SQLAlchemy | Column(Integer, ...) |
Mapped[int] = mapped_column(...) |
| Создание сессии | sessionmaker(bind=engine) + Session() + close() |
with Session(engine) as session: |
Важно: в интернете огромное количество примеров написано на Pydantic v1 и SQLAlchemy 1.x. Если вы видите
@validator, constr, condecimal, declarative_base() или sessionmaker(bind=engine) — это старый код. В нашем курсе файлы examples, tasks, solutions, homework используют актуальные версии.