← Назад к вопросам

Что такое аннотация в ORM?

2.0 Middle🔥 201 комментариев
#Django#Базы данных (SQL)

Комментарии (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")

Зачем нужны аннотации?

  1. Валидация данных — БД проверяет ограничения (NOT NULL, UNIQUE)
  2. Производительность — индексы ускоряют поиск
  3. Целостность данных — внешние ключи и check constraints
  4. Документация — код читается как описание таблицы
  5. Автоматизация — ORM использует аннотации для создания SQL

Вывод

Аннотации в ORM — это описание поведения полей и их ограничений. Они превращают модель Python в полноценное описание таблицы БД, позволяя ORM автоматически создавать SQL и обеспечивать целостность данных.