Reputation: 31
I am facing an issue when trying to generate migrations using Alembic and SQLAlchemy. My project is structured as follows:
I have a folder called model
where each entity (model) is defined in a separate file.
I use a config
folder containing a file called register.py
, where I initialize an instance of registry()
and store it in a variable named table_register
.
In my models, I use the @table_register.mapped_as_data_class
decorator to map the classes to database tables.
The problem occurs when I run the command:
The revision file is generated, but both the upgrade
and downgrade
functions are empty. It seems that Alembic is not detecting the tables mapped in my project.
register.py file:
from sqlalchemy.orm import registry
table_register = registry()
metadata = table_register.metadata
Example model:
from enum import Enum
from datetime import datetime
import sqlalchemy
from sqlalchemy import (String, func, ForeignKey)
from sqlalchemy.orm import (Mapped, mapped_column)
from configs.register import table_register
@table_register.mapped_as_dataclass
class Aluno():
__tablename__ = 'aluno'
id: Mapped[int] = mapped_column(primary_key=True, init=False, name='id_aluno')
nome: Mapped[str] = mapped_column(String(100), nullable=False, name='nome_aluno')
serie: Mapped[int] = mapped_column(ForeignKey('serie.id_serie'), nullable=False, name='serie_aluno')
turma: Mapped[Enum] = mapped_column(sqlalchemy.Enum('A', 'B', name='turma_enum'), nullable=False, name='turma_aluno')
created_at: Mapped[datetime] = mapped_column(name='created_at', server_default=func.now(), init=False)
Alembic Config:
config = context.config
config.set_main_option('sqlalchemy.url', Config().DB_URI)
target_metadata = table_register.metadata
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,
compare_type=True
)
with context.begin_transaction():
context.run_migrations()
Even though everything seems to be configured correctly, Alembic does not detect the tables and generates empty migrations. I am unsure if I am missing something in the Alembic configuration or if there is a specific requirement for using SQLAlchemy's registry()
.
What could be causing this issue? Is there any additional configuration needed to make Alembic recognize tables defined using registry.mapped_as_data_class
?
Upvotes: 0
Views: 89
Reputation: 302
I had this same issue a couple years ago when first using alembic. Now I do not remember the exact solution but this is my config.
from os import getenv
from dotenv import load_dotenv
load_dotenv()
import sys
import os.path
# append parent dir to path to expose imports
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
# db.py
from db import DB_BASE
from logging.config import fileConfig
from sqlalchemy import engine_from_config
from sqlalchemy import pool
from alembic import context
config = context.config
uri = getenv('DATABASE_URI')
if uri.startswith('postgres://'):
uri = uri.replace('postgres://', 'postgresql://', 1)
config.set_main_option("sqlalchemy.url", uri)
fileConfig(config.config_file_name)
target_metadata = DB_BASE.metadata
def run_migrations_offline():
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
render_as_batch=True,
)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online():
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()
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
uri = "YOUR_URI"
DB_ENGINE = create_engine(uri)
DB_SES_LOCAL = sessionmaker(bind=DB_ENGINE, autoflush=False)
DB_BASE = declarative_base()
from models import *
DB_BASE.metadata.create_all(DB_ENGINE)
Upvotes: 0