Что такое декларативный маппинг в SQLAlchemy

Ответ

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

Этот подход противопоставляется более старому, классическому маппингу, где структура таблицы и класс определялись раздельно.

Ключевые компоненты:

  • declarative_base(): Фабричная функция, которая создает базовый класс для всех моделей.
  • __tablename__: Атрибут класса, указывающий имя таблицы в БД.
  • Column: Объект, описывающий колонку таблицы, ее тип и ограничения.
  • relationship: Определяет связь между моделями (один-ко-многим, многие-ко-многим).

Пример: модели User и Post со связью "один-ко-многим"

from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship, sessionmaker, declarative_base

# 1. Создание базового класса для моделей
Base = declarative_base()

# 2. Определение модели User
class User(Base):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True)
    name = Column(String(50), nullable=False)

    # Связь: у одного пользователя может быть много постов
    posts = relationship("Post", back_populates="author")

# 3. Определение модели Post
class Post(Base):
    __tablename__ = 'posts'

    id = Column(Integer, primary_key=True)
    title = Column(String(100), nullable=False)
    user_id = Column(Integer, ForeignKey('users.id'))

    # Обратная связь: у поста есть один автор
    author = relationship("User", back_populates="posts")

# Использование:
# engine = create_engine('sqlite:///:memory:')
# Base.metadata.create_all(engine)

Преимущества декларативного подхода:

  • Читаемость и поддержка: Код становится интуитивно понятным, так как структура таблицы и ее программное представление находятся в одном классе.
  • Простота: Значительно сокращает количество шаблонного кода по сравнению с классическим подходом.
  • Интеграция: Является стандартом де-факто и отлично интегрируется с инструментами миграций, такими как Alembic.

Ответ 18+ 🔞

А, ну так вот, слушай, про этот ваш декларативный маппинг в SQLAlchemy. Это ж, блядь, как будто взяли и наконец-то по-человечески сделали, а не как раньше, когда мозги выносило на раз-два.

Раньше-то, классический маппинг, это был пиздец, блядь. Отдельно таблицу описываешь, как будто в каменном веке, отдельно класс пишешь, а потом их ещё склеить надо. А тут — ёпта, красота! Всё в одном месте, в одном классе. И таблица, и модель, и логика — всё, сука, вместе. Как будто тебе дали конструктор "Лего" вместо кучи разрозненных палок и говна.

Вот смотри, из чего этот конструктор состоит, главные детальки:

  • declarative_base(): Это типа волшебная палочка, которая создаёт тебе базовый класс. От него все твои будущие модели будут наследоваться. Без него нихуя не начнётся.
  • __tablename__: Это ты пишешь прямо в классе, как таблицу в базе назвать. Без этого SQLAlchemy начнёт выёбываться и придумывать имя сама, а это обычно пиздец как криво получается.
  • Column: Ну это, блядь, столбец таблицы. Тут ты указываешь, что это за хуйня: Integer, String, и какие ограничения: primary_key=True, nullable=False (то есть, чтоб не пустое было, а то потом охуеешь искать баг).
  • relationship: А вот это уже магия поинтереснее. Это связь между моделями. Типа "один пользователь — много постов". Без неё ты будешь вручную JOIN-ы писать, как лох.

Ладно, хватит болтать, вот тебе живой пример: Пользователь и его посты (один ко многим).

from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship, sessionmaker, declarative_base

# 1. Тыкаем волшебную палочку — создаём базовый класс
Base = declarative_base()

# 2. Делаем модель Юзера. Всё в одном классе, блядь, удобно же!
class User(Base):
    __tablename__ = 'users' # Название таблицы в базе

    id = Column(Integer, primary_key=True) # ID, главный ключ, ёпта
    name = Column(String(50), nullable=False) # Имя, и чтоб не пустое, сука!

    # А вот и магия связи! Говорим: "У этого пользователя есть посты".
    # "Post" — это название класса модели поста.
    # back_populates="author" — это чтобы из поста тоже можно было до юзера достучаться.
    posts = relationship("Post", back_populates="author")

# 3. Теперь модель Поста
class Post(Base):
    __tablename__ = 'posts'

    id = Column(Integer, primary_key=True)
    title = Column(String(100), nullable=False)
    user_id = Column(Integer, ForeignKey('users.id')) # Ссылаемся на юзера

    # Обратная связь. Говорим: "У этого поста есть автор".
    # Ссылаемся на класс "User", а конкретно на его атрибут "posts".
    author = relationship("User", back_populates="posts")

# Ну и как это использовать, блядь:
# engine = create_engine('sqlite:///:memory:') # Создаём движок для базы
# Base.metadata.create_all(engine) # Эта команда, ёпта, создаст все таблицы в базе по нашим моделям. Красота!

И в чём же, блядь, охуенность этого подхода?

  • Читаемость заебись: Открыл один файл, один класс — и сразу видно, какая таблица, какие поля, какие связи. Не надо прыгать между пятью разными местами, ебать твою мать.
  • Простота, блять: Раньше кода было как говна за баней, а теперь — чисто, аккуратно, по делу. Меньше писанины — меньше ошибок.
  • Всё дружит: Этот подход — как родной для всех современных инструментов. Тот же Alembic для миграций на него смотрит и говорит: "О, бля, знакомый, сейчас всё автоматически нагенерирую". И правда нагенерирует, без твоих кривых рук.

В общем, если ты до сих пор юзаешь что-то другое — ты просто, извини, еблан. Всё, точка.