Alembic создаёт пустые миграции

Проблема: после alembic revision --autogenerate создаётся пустая миграция (модели не подгрузились). Скорее всего, alembic не видит модели. Поэтому я решил все в один файл засунуть, т.е. в wallet_model, но это тоже не помогло.

Файл wallet_model:

from sqlalchemy import Column, Numeric, DateTime, text
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.sql import func
from sqlalchemy.orm import declarative_base


# База для моделей
Base = declarative_base()


class Wallet(Base):
    __tablename__ = "wallets"

    id = Column(
        UUID(as_uuid=True),
        primary_key=True,
        server_default=text("gen_random_uuid()")
    )
    balance = Column(
        Numeric(15, 2),
        nullable=False,
        default=0.00,
        server_default="0.00"
    )
    created_at = Column(
        DateTime(timezone=True),
        server_default=func.now()
    )
    updated_at = Column(
        DateTime(timezone=True),
        onupdate=func.now()
    )

Файл env.py:

from logging.config import fileConfig

from dotenv import load_dotenv
from os import getenv

from sqlalchemy import engine_from_config
from sqlalchemy import pool

from alembic import context

from models.wallet_model import Base
from models.wallet_model import Wallet


config = context.config

load_dotenv()

POSTGRES_USER = getenv('POSTGRES_USER')
POSTGRES_PASS = getenv('POSTGRES_PASSWORD')
POSTGRES_HOST = getenv('POSTGRES_HOST')
POSTGRES_PORT = getenv('POSTGRES_PORT')
POSTGRES_DB = getenv('POSTGRES_DB')



DB_URL = (
    f"postgresql+asyncpg://{POSTGRES_USER}:{POSTGRES_PASS}"
    f"@{POSTGRES_HOST}:{POSTGRES_PORT}/{POSTGRES_DB}"
)

if config.config_file_name is not None:
    fileConfig(config.config_file_name)

inclued_schemas = True

config.set_main_option("sqlalchemy.url", DB_URL + "?async_fallback=True")

target_metadata = Base.metadata


def run_migrations_offline() -> None:
    """Run migrations in 'offline' mode.

    This configures the context with just a URL
    and not an Engine, though an Engine is acceptable
    here as well.  By skipping the Engine creation
    we don't even need a DBAPI to be available.

    Calls to context.execute() here emit the given string to the
    script output.

    """
    url = config.get_main_option("sqlalchemy.url")
    context.configure(
        url=url,
        target_metadata=target_metadata,
        literal_binds=True,
        dialect_opts={"paramstyle": "named"},
    )

    with context.begin_transaction():
        context.run_migrations()


def run_migrations_online() -> None:
    """Run migrations in 'online' mode.

    In this scenario we need to create an Engine
    and associate a connection with the context.

    """
    connectable = engine_from_config(
        config.get_section(config.config_ini_section, {}),
        prefix="sqlalchemy.",
        poolclass=pool.NullPool,
    )

    with connectable.connect() as connection:
        context.configure(
            connection=connection, target_metadata=target_metadata
        )

        with context.begin_transaction():
            context.run_migrations()


if context.is_offline_mode():
    run_migrations_offline()
else:
    run_migrations_online()

alembic.ini:

[alembic]
script_location = src/alembic

prepend_sys_path = . src

version_path_separator = os

sqlalchemy.url = driver://user:pass@localhost/dbname


[loggers]
keys = root,sqlalchemy,alembic

[handlers]
keys = console

[formatters]
keys = generic

[logger_root]
level = WARNING
handlers = console
qualname =

[logger_sqlalchemy]
level = WARNING
handlers =
qualname = sqlalchemy.engine

[logger_alembic]
level = INFO
handlers =
qualname = alembic

[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic

[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S

Структура:

│   .env
│   .env example
│   .gitignore
│   alembic.ini
│   docker-compose.yml
│   Dockerfile
│   README.md
│   requirements.txt
│
├───.pytest_cache
│   │   .gitignore
│   │   CACHEDIR.TAG
│   │   README.md
│   │
│   └───v
│       └───cache
│               lastfailed
│               nodeids
│               stepwise
│
├───src
│   │   main.py
│   │   setup.py
│   │   __init__.py
│   │
│   ├───alembic
│   │   │   env.py
│   │   │   script.py.mako
│   │   │
│   │   ├───versions
│   │   │   └───__pycache__
│   │   │           3d6ca513c0f2_initial.cpython-311.pyc
│   │   │           d9fa35d931d1_.cpython-311.pyc
│   │   │
│   │   └───__pycache__
│   │           env.cpython-311.pyc
│   │
│   ├───api
│   │   │   __init__.py
│   │   │
│   │   ├───v1
│   │   │   │   __init__.py
│   │   │   │
│   │   │   ├───wallets
│   │   │   │   │   wallet_router.py
│   │   │   │   │   __init__.py
│   │   │   │   │
│   │   │   │   └───__pycache__
│   │   │   │           wallet_router.cpython-311.pyc
│   │   │   │           __init__.cpython-311.pyc
│   │   │   │
│   │   │   └───__pycache__
│   │   │           __init__.cpython-311.pyc
│   │   │
│   │   └───__pycache__
│   │           __init__.cpython-311.pyc
│   │
│   ├───database
│   │   │   session.py
│   │   │   __init__.py
│   │   │
│   │   └───__pycache__
│   │           session.cpython-311.pyc
│   │           __init__.cpython-311.pyc
│   │
│   ├───models
│   │   │   wallet_model.py
│   │   │   __init__.py
│   │   │
│   │   └───__pycache__
│   │           wallet_model.cpython-311.pyc
│   │           __init__.cpython-311.pyc
│   │
│   ├───schemas
│   │   │   base_schema.py
│   │   │   wallet_schema.py
│   │   │   __init__.py
│   │   │
│   │   └───__pycache__
│   │           wallet_schema.cpython-311.pyc
│   │           __init__.cpython-311.pyc
│   │
│   ├───services
│   │   │   wallet_service.py
│   │   │   __init__.py
│   │   │
│   │   └───__pycache__
│   │           wallet_service.cpython-311.pyc
│   │           __init__.cpython-311.pyc
│   │
│   ├───wallet_api.egg-info
│   │       dependency_links.txt
│   │       PKG-INFO
│   │       requires.txt
│   │       SOURCES.txt
│   │       top_level.txt
│   │
│   └───__pycache__
│           main.cpython-311.pyc
│           __init__.cpython-311.pyc
│
└───tests
    │   conftest.py
    │   test_wallet_api.py
    │   __init__.py
    │
    ├───.pytest_cache
    │   │   .gitignore
    │   │   CACHEDIR.TAG
    │   │   README.md
    │   │
    │   └───v
    │       └───cache
    │               lastfailed
    │               nodeids
    │               stepwise
    │
    └───__pycache__
            conftest.cpython-311-pytest-8.3.4.pyc
            conftest.cpython-311-pytest-8.3.5.pyc
            test_wallet_api.cpython-311-pytest-8.3.4.pyc
            test_wallet_api.cpython-311-pytest-8.3.5.pyc
            __init__.cpython-311.pyc

Ответы (1 шт):

Автор решения: DD Alter

Прошерстив интернет я узнал, что если таблица уже создана, то миграция изначальна будет пустой. Почему я не обратил на это внимание? Потому что подумал, что если БД хоть и создана, но пустая, то миграции все равно задетектят таблицы, но нет. Решил проблему удалением таблицы (благо это была тестовая таблица). После этого все заработало и alembic задетектил всё что нужно.

→ Ссылка