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
Примените новую миграцию, выполнив:
Пояснение:
conn = op.get_bind()
: Получает соединение с базой данных для выполнения SQL-запросов напрямую.ON CONFLICT (key) DO NOTHING
: PostgreSQL-конструкция для игнорирования ошибок вставки, если запись с таким ключом уже существует (аналогичноINSERT OR IGNORE
в SQLite).- Использование
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()
Рекомендации:
- Для разового выполнения начальной инициализации используйте Alembic миграции.
- Если данные должны проверяться и обновляться динамически, создайте отдельную функцию для инициализации, как в примере с 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()
Пояснения:
Session(bind=bind)
: Создаёт сессию на основе текущего соединения Alembic.- Проверка на существование таблицы: Это полезно, если вы не уверены, что таблица уже создана.
- Проверка существующих записей: Гарантирует, что данные не будут добавлены повторно, если миграция выполняется несколько раз.
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 в одном из миграционных скриптов, как показано в первом примере.