Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Аннотации в ORM
Краткий ответ
Аннотация в ORM (Object-Relational Mapping) — это дополнительные метаданные, которые добавляют информацию к полям модели для управления её поведением в контексте базы данных.
Аннотации описывают:
- Типы данных
- Ограничения (constraints)
- Отношения между таблицами
- Поведение при операциях с БД
На примере SQLAlchemy
Базовый пример
from sqlalchemy import Column, Integer, String, Boolean, DateTime
from sqlalchemy.orm import declarative_base, Relationship
from datetime import datetime
Base = declarative_base()
class User(Base):
__tablename__ = "users"
# Аннотация: первичный ключ
id = Column(Integer, primary_key=True)
# Аннотация: обязательное строковое поле
name = Column(String(255), nullable=False)
# Аннотация: поле с дефолтным значением
email = Column(String(255), unique=True, nullable=False)
# Аннотация: булевское поле с дефолтом
is_active = Column(Boolean, default=True)
# Аннотация: временная метка с дефолтом
created_at = Column(DateTime, default=datetime.utcnow)
# Аннотация: отношение к другой таблице
posts = Relationship("Post", back_populates="author")
Что означают аннотации?
# primary_key=True
# → Это поле будет первичным ключом (уникально, индексировано)
id = Column(Integer, primary_key=True)
# nullable=False
# → Это поле обязательно (NOT NULL в SQL)
name = Column(String(255), nullable=False)
# unique=True
# → Значения в этом поле должны быть уникальными
email = Column(String(255), unique=True)
# default=<value>
# → Значение по умолчанию при создании записи
is_active = Column(Boolean, default=True)
# index=True
# → Создать индекс на это поле для быстрого поиска
status = Column(String(50), index=True)
Аннотации отношений (Relationships)
from sqlalchemy import ForeignKey
from sqlalchemy.orm import Relationship
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
name = Column(String)
# Аннотация отношения: один пользователь — много постов
posts = Relationship(
"Post",
back_populates="author", # Обратная ссылка
cascade="all, delete-orphan", # Удалить посты при удалении пользователя
lazy="select" # Загрузить посты при запросе пользователя
)
class Post(Base):
__tablename__ = "posts"
id = Column(Integer, primary_key=True)
title = Column(String)
# Аннотация: внешний ключ
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
# Аннотация отношения: много постов — один пользователь
author = Relationship(
"User",
back_populates="posts" # Двусторонняя ссылка
)
Аннотации в Pydantic + SQLAlchemy
SQLAlchemy модель
from sqlalchemy import Column, Integer, String, DateTime
from sqlalchemy.orm import declarative_base
from datetime import datetime
Base = declarative_base()
class UserDB(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True) # Аннотация БД
name = Column(String(255), nullable=False) # Обязательное
email = Column(String(255), unique=True) # Уникальное
created_at = Column(DateTime, default=datetime.utcnow) # С дефолтом
Pydantic схема
from pydantic import BaseModel, Field, EmailStr
from datetime import datetime
class UserSchema(BaseModel):
# Аннотация: тип данных + валидация
id: int
# Аннотация: строка, минимум 1 символ
name: str = Field(..., min_length=1, max_length=255)
# Аннотация: email с валидацией
email: EmailStr
# Аннотация: опциональное поле с дефолтом
created_at: datetime = Field(default_factory=datetime.utcnow)
class Config:
from_attributes = True # Преобразовать ORM объект в Pydantic
Типы аннотаций в SQLAlchemy
# Тип данных + Ограничения
from sqlalchemy import (
Column, Integer, String, Boolean, DateTime,
Numeric, Text, JSON, ARRAY
)
class Product(Base):
__tablename__ = "products"
# Целое число
id = Column(Integer, primary_key=True)
# Строка с максимальной длиной
name = Column(String(255), nullable=False, unique=True)
# Длинный текст
description = Column(Text)
# Число с фиксированной точностью
price = Column(Numeric(10, 2), nullable=False) # 10 цифр, 2 после запятой
# JSON данные
metadata = Column(JSON)
# Массив (PostgreSQL)
tags = Column(ARRAY(String))
# Булевское значение
is_available = Column(Boolean, default=True)
# Дата и время
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, onupdate=datetime.utcnow)
Аннотации для индексов
from sqlalchemy import Index
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
# Индекс на одно поле
email = Column(String, index=True)
status = Column(String, index=True)
# Составной индекс (несколько полей)
__table_args__ = (
Index("idx_email_status", "email", "status"),
Index("idx_created", "created_at", postgresql_using="btree"),
)
created_at = Column(DateTime, default=datetime.utcnow)
Аннотации проверки данных
from sqlalchemy import CheckConstraint
class Product(Base):
__tablename__ = "products"
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
# Цена не может быть меньше нуля
price = Column(Numeric(10, 2), nullable=False)
# Проверка: quantity >= 0
quantity = Column(Integer, CheckConstraint("quantity >= 0"))
__table_args__ = (
CheckConstraint("price > 0", name="check_positive_price"),
)
Практический пример: E-commerce модель
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, Numeric, Enum
from sqlalchemy.orm import declarative_base, Relationship
from datetime import datetime
import enum
Base = declarative_base()
class OrderStatus(enum.Enum):
PENDING = "pending"
CONFIRMED = "confirmed"
SHIPPED = "shipped"
DELIVERED = "delivered"
class User(Base):
__tablename__ = "users"
# Первичный ключ
id = Column(Integer, primary_key=True)
# Уникальный, обязательный email
email = Column(String(255), unique=True, nullable=False, index=True)
# Обязательное имя
name = Column(String(255), nullable=False)
# Статус активности с дефолтом
is_active = Column(Boolean, default=True)
# Временные метки
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# Отношение: один юзер — много заказов
orders = Relationship("Order", back_populates="user", cascade="all, delete-orphan")
class Order(Base):
__tablename__ = "orders"
id = Column(Integer, primary_key=True)
# Внешний ключ + индекс
user_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True)
# Статус заказа из enum
status = Column(Enum(OrderStatus), default=OrderStatus.PENDING, nullable=False)
# Сумма заказа
total_amount = Column(Numeric(10, 2), nullable=False)
# Временные метки
created_at = Column(DateTime, default=datetime.utcnow, index=True)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# Обратное отношение к пользователю
user = Relationship("User", back_populates="orders")
Зачем нужны аннотации?
- Валидация данных — БД проверяет ограничения (NOT NULL, UNIQUE)
- Производительность — индексы ускоряют поиск
- Целостность данных — внешние ключи и check constraints
- Документация — код читается как описание таблицы
- Автоматизация — ORM использует аннотации для создания SQL
Вывод
Аннотации в ORM — это описание поведения полей и их ограничений. Они превращают модель Python в полноценное описание таблицы БД, позволяя ORM автоматически создавать SQL и обеспечивать целостность данных.