AI Notes
January 19

Alembic миграции

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

Вот пошаговый подход для добавления первых данных в таблицу:

1. Создайте миграцию с Alembic

Используйте команду alembic revision --autogenerate -m "Добавление начальных данных" или создайте новую миграцию вручную:

alembic revision -m "Добавление начальных данных"

2. Добавьте код для вставки данных в миграцию

В созданном файле миграции найдите функцию upgrade и добавьте SQL-запросы или ORM-операции для вставки данных.

Пример файла миграции (<timestamp>_добавление_начальных_данных.py):

from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = '<revision_id>'
down_revision = '<previous_revision_id>'
branch_labels = None
depends_on = None

def upgrade():
    # Добавление начальных данных
    conn = op.get_bind()
    conn.execute(
        sa.text(
            "INSERT INTO settings (key, value) VALUES (:key, :value) ON CONFLICT (key) DO NOTHING"
        ),
        [{"key": "is_mode_get_user_id", "value": "0"},
         {"key": "get_user_id_message", "value": "😶😶😶"}]
    )
    
def downgrade():
    # Удаление данных (опционально, если требуется при откате)
    conn = op.get_bind()
    conn.execute(
        sa.text("DELETE FROM settings WHERE key IN (:keys)"),
        {"keys": ("is_mode_get_user_id", "get_user_id_message")}
    )

3. Обновите базу данных с помощью Alembic

Примените новую миграцию, выполнив:

alembic upgrade head

Пояснение:

  1. conn = op.get_bind(): Получает соединение с базой данных для выполнения SQL-запросов напрямую.
  2. ON CONFLICT (key) DO NOTHING: PostgreSQL-конструкция для игнорирования ошибок вставки, если запись с таким ключом уже существует (аналогично INSERT OR IGNORE в SQLite).
  3. Использование sa.text: Позволяет вставлять данные через сырые SQL-запросы с поддержкой параметров.

Альтернативный подход через ORM (опционально)

Если вы хотите использовать SQLAlchemy ORM вместо сырых SQL-запросов, можно выполнить инициализацию в отдельном скрипте, используя сессию:

from sqlalchemy.orm import Session
from your_project.models import Setting  # Импортируйте вашу ORM-модель

def initialize_settings(engine):
    with Session(engine) as session:
        # Проверяем, есть ли записи, и добавляем только если они отсутствуют
        if not session.query(Setting).filter(Setting.key == "is_mode_get_user_id").first():
            session.add(Setting(key="is_mode_get_user_id", value="0"))
        if not session.query(Setting).filter(Setting.key == "get_user_id_message").first():
            session.add(Setting(key="get_user_id_message", value="😶😶😶"))
        session.commit()

Рекомендации:

  1. Для разового выполнения начальной инициализации используйте Alembic миграции.
  2. Если данные должны проверяться и обновляться динамически, создайте отдельную функцию для инициализации, как в примере с ORM.

Использовать Alembic или ORM для внесения начальных данных возможно, и оба подхода имеют свои плюсы. Вот детальное руководство для обоих случаев.

1. Внесение данных через Alembic

В миграциях Alembic вы можете использовать SQLAlchemy ORM для работы с таблицами.

Пример файла миграции (<timestamp>_добавление_данных.py):

from alembic import op
from sqlalchemy.orm import Session
from sqlalchemy import inspect
from your_project.models import Settings  # Импортируйте вашу модель Settings

# revision identifiers, used by Alembic.
revision = '<revision_id>'
down_revision = '<previous_revision_id>'
branch_labels = None
depends_on = None

def upgrade():
    # Получение текущей сессии
    bind = op.get_bind()
    session = Session(bind=bind)
    # Проверка существования таблицы (опционально)
    inspector = inspect(bind)
    if 'settings' in inspector.get_table_names():
        # Вставка данных, если таблица существует
        settings = [
            Settings(key='is_mode_get_user_id', value='0'),
            Settings(key='get_user_id_message', value='😶😶😶'),
        ]
        for setting in settings:
            # Убедитесь, что данные добавляются только при отсутствии
            if not session.query(Settings).filter_by(key=setting.key).first():
                session.add(setting)
        session.commit()
        
def downgrade():
    # Откат: удаление данных
    bind = op.get_bind()
    session = Session(bind=bind)
        session.query(Settings).filter(
        Settings.key.in_(['is_mode_get_user_id', 'get_user_id_message'])
    ).delete(synchronize_session=False)
    session.commit()    

Пояснения:

  1. Session(bind=bind): Создаёт сессию на основе текущего соединения Alembic.
  2. Проверка на существование таблицы: Это полезно, если вы не уверены, что таблица уже создана.
  3. Проверка существующих записей: Гарантирует, что данные не будут добавлены повторно, если миграция выполняется несколько раз.

2. Внесение данных через ORM

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

Пример:

from sqlalchemy.orm import Session
from your_project.models import Settings
from sqlalchemy import create_engine
from your_project.database import Base  # где инициализируется ваш Base

# Создаём подключение к базе данных
engine = create_engine("postgresql+psycopg2://username:password@localhost/dbname")

def initialize_settings():
    with Session(engine) as session:
        # Добавляем данные, если они ещё не существуют
        if not session.query(Settings).filter_by(key="is_mode_get_user_id").first():
            session.add(Settings(key="is_mode_get_user_id", value="0"))
        if not session.query(Settings).filter_by(key="get_user_id_message").first():
            session.add(Settings(key="get_user_id_message", value="😶😶😶"))
        session.

# Вызываем функцию инициализации
if __name__ == "__main__":
    initialize_settings()

Что выбрать?

  • Alembic миграции: Если данные должны быть добавлены вместе с обновлением структуры базы данных. Это лучший подход для контроля версий.
  • ORM: Если данные должны добавляться в рантайме, например, при первом запуске приложения.

Комбинированный подход:

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