diff --git a/migrations/env.py b/migrations/env.py deleted file mode 100644 index c53bac2..0000000 --- a/migrations/env.py +++ /dev/null @@ -1,79 +0,0 @@ -# - *- coding: utf- 8 - *- -import asyncio -from contextlib import AbstractAsyncContextManager -from logging.config import fileConfig - -from alembic import context -from sqlalchemy import pool -from sqlalchemy.engine import Connection -from sqlalchemy.ext.asyncio import AsyncConnection, AsyncEngine, async_engine_from_config - -from tgbot.database.core import Base - -# Импортируем модели, чтобы Alembic видел все таблицы -import tgbot.database # noqa: F401 - -config = context.config - -if config.config_file_name is not None: - fileConfig(config.config_file_name) - -target_metadata = Base.metadata - - -# Настройка миграций без подключения к базе -def run_migrations_offline() -> None: - url = config.get_main_option("sqlalchemy.url") - - context.configure( - url=url, - target_metadata=target_metadata, - literal_binds=True, - dialect_opts={"paramstyle": "named"}, - compare_type=True, - ) - - with context.begin_transaction(): - context.run_migrations() - - -# Настройка миграций поверх готового соединения -def do_run_migrations(connection: Connection) -> None: - context.configure( - connection=connection, - target_metadata=target_metadata, - compare_type=True, - ) - - with context.begin_transaction(): - context.run_migrations() - - -# Создаем async-engine и передаем Alembic синхронное соединение внутри run_sync -async def run_async_migrations() -> None: - connectable = async_engine_from_config( - config.get_section(config.config_ini_section, {}), - prefix="sqlalchemy.", - poolclass=pool.NullPool, - ) - - async with connectable_context(connectable) as connection: - await connection.run_sync(do_run_migrations) - - await connectable.dispose() - - -# Отдельная обертка помогает IDE правильно определить async context manager -def connectable_context(connectable: AsyncEngine) -> AbstractAsyncContextManager[AsyncConnection]: - return connectable.connect() - - -if context.is_offline_mode(): - run_migrations_offline() -else: - external_connection = config.attributes.get("connection") - - if external_connection is not None: - do_run_migrations(external_connection) - else: - asyncio.run(run_async_migrations()) diff --git a/migrations/script.py.mako b/migrations/script.py.mako deleted file mode 100644 index 590f5b3..0000000 --- a/migrations/script.py.mako +++ /dev/null @@ -1,24 +0,0 @@ -"""${message} - -Revision ID: ${up_revision} -Revises: ${down_revision | comma,n} -Create Date: ${create_date} -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa -${imports if imports else ""} - -revision: str = ${repr(up_revision)} -down_revision: Union[str, None] = ${repr(down_revision)} -branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} -depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} - - -def upgrade() -> None: - ${upgrades if upgrades else "pass"} - - -def downgrade() -> None: - ${downgrades if downgrades else "pass"} diff --git a/migrations/versions/20260528_0001_initial_schema.py b/migrations/versions/20260528_0001_initial_schema.py deleted file mode 100644 index 6070258..0000000 --- a/migrations/versions/20260528_0001_initial_schema.py +++ /dev/null @@ -1,134 +0,0 @@ -"""Начальная схема проекта - -Revision ID: 0001_initial_schema -Revises: -Create Date: 2026-05-28 00:00:00 -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa - -revision: str = "0001_initial_schema" -down_revision: Union[str, None] = None -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -# Проверяем таблицу через sqlite_master, потому что первая миграция должна принять и старую БД -def _table_exists(table_name: str) -> bool: - bind = op.get_bind() - result = bind.execute( - sa.text( - """ - SELECT name - FROM sqlite_master - WHERE type = 'table' - AND name = :table_name - """ - ), - {"table_name": table_name}, - ) - - return result.scalar_one_or_none() is not None - - -# Проверяем колонку перед миграцией старой таблицы -def _column_exists(table_name: str, column_name: str) -> bool: - bind = op.get_bind() - result = bind.execute(sa.text(f"PRAGMA table_info({table_name})")) - - return column_name in [row.name for row in result] - - -def upgrade() -> None: - bind = op.get_bind() - - if _table_exists("storage_users"): - bind.execute(sa.text("DELETE FROM storage_users WHERE user_id IS NULL")) - bind.execute( - sa.text( - """ - DELETE - FROM storage_users - WHERE user_id IS NOT NULL - AND rowid NOT IN (SELECT MAX(rowid) AS last_rowid - FROM storage_users - WHERE user_id IS NOT NULL - GROUP BY user_id) - """ - ) - ) - else: - op.create_table( - "storage_users", - sa.Column("increment", sa.Integer(), autoincrement=True, nullable=False), - sa.Column("user_id", sa.BigInteger(), nullable=False), - sa.Column("user_login", sa.String(length=255), nullable=False, server_default=""), - sa.Column("user_name", sa.String(length=255), nullable=False, server_default=""), - sa.Column("user_surname", sa.String(length=255), nullable=False, server_default=""), - sa.Column("user_fullname", sa.String(length=511), nullable=False, server_default=""), - sa.Column("user_unix", sa.Integer(), nullable=False), - sa.PrimaryKeyConstraint("increment"), - ) - - op.create_index("ix_storage_users_user_id", "storage_users", ["user_id"], unique=True, if_not_exists=True) - - if _table_exists("storage_settings"): - status_source = "status_work" if _column_exists("storage_settings", "status_work") else "'false'" - bind.execute(sa.text("DROP TABLE IF EXISTS storage_settings_new")) - bind.execute( - sa.text( - """ - CREATE TABLE storage_settings_new - ( - id INTEGER NOT NULL PRIMARY KEY, - status_work BOOLEAN NOT NULL DEFAULT 0 - ) - """ - ) - ) - bind.execute( - sa.text( - """ - INSERT INTO storage_settings_new (id, status_work) - SELECT 1 AS id, - CASE LOWER(CAST(COALESCE({status_source}, 'false') AS TEXT)) - WHEN '1' THEN 1 - WHEN 'true' THEN 1 - WHEN 'yes' THEN 1 - WHEN 'on' THEN 1 - WHEN 'да' THEN 1 - ELSE 0 - END AS status_work - FROM storage_settings - LIMIT 1 - """ - .format(status_source=status_source) - ) - ) - bind.execute(sa.text("DROP TABLE storage_settings")) - bind.execute(sa.text("ALTER TABLE storage_settings_new RENAME TO storage_settings")) - else: - op.create_table( - "storage_settings", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("status_work", sa.Boolean(), nullable=False, server_default=sa.text("0")), - sa.PrimaryKeyConstraint("id"), - ) - - bind.execute( - sa.text( - """ - INSERT INTO storage_settings (id, status_work) - SELECT 1 AS id, 0 AS status_work - WHERE NOT EXISTS (SELECT 1 AS exists_status FROM storage_settings WHERE id = 1) - """ - ) - ) - - -def downgrade() -> None: - op.drop_table("storage_settings") - op.drop_index("ix_storage_users_user_id", table_name="storage_users") - op.drop_table("storage_users")