⚖️ Старый vs Новый: Pydantic v1 + SQLAlchemy 1.x (лекция) vs v2 + 2.x

Лекция написана на старых API — здесь показан переход на современный код

⚡ Кратко: что изменилось

  • Pydantic: @validator@field_validator + @classmethod; constr/condecimalAnnotated[..., Field(...)]; class Configmodel_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 используют актуальные версии.