即使没有任何更改,Alembic 也会继续创建空的迁移文件

Alembic keeps creating empty migration files even tho there are no changes

我正在使用 sqlalchemy、postgres 和 alembic 开发一个应用程序。
项目结构如下:

.
├── alembic.ini
├── main.py
├── migrations
│   ├── env.py
│   ├── README
│   ├── script.py.mako
│   └── versions
├── models
│   ├── base.py
│   ├── datamodel1.py
│   ├── datamodel2.py
│   └── __init__.py
└── requirements.txt

3 directories, 10 files

其中:
models/base.py 的内容是:

from sqlalchemy.ext.declarative.api import declarative_base, DeclarativeMeta

Base: DeclarativeMeta = declarative_base()

models/datamodel1.py的内容是:

from models.base import Base
from sqlalchemy.sql.schema import Column
from sqlalchemy.sql.sqltypes import String, Date, Float


class Model1(Base):
    __tablename__ = 'model1_table'

    model1_id = Column(String, primary_key=True)
    col1 = Column(String)
    col2 = Column(String)

models/datamodel2.py的内容是:

from models.base import Base
from sqlalchemy.orm import relationship
from sqlalchemy.sql.sqltypes import String, Integer, Date
from sqlalchemy.sql.schema import Column, ForeignKey


# The many to may relationship table
class Model1Model2(Base):
    __tablename__ = 'model1_model2_table'

    id = Column(Integer, primary_key=True)
    model_1_id = Column(String, ForeignKey('model1.model1_id'))
    model_2_id = Column(Integer, ForeignKey('model2.model2_id'))


class Model2(Base):
    __tablename__ = 'model2_table'

    model2_id = Column(Integer, primary_key=True)
    model2_col1 = Column(String)
    model2_col2 = Column(Date)
    # Many to many relationship
    model1_model2 = relationship('Model1', secondary='model1_model2_table', backref='model1_table')

migrations/env.py的内容是:

from logging.config import fileConfig

from sqlalchemy import engine_from_config
from sqlalchemy import pool

from alembic import context
import sys
sys.path.append('./')



# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.

config = context.config

# I added the following 2 lines to replace the sqlalchemy.url in alembic.ini file.  
db_string = f'postgresql+psycopg2://{db_username}:{db_password}@{db_host}:{db_port}/{db_name}'
config.set_main_option('sqlalchemy.url', db_string)

# Interpret the config file for Python logging.
# This line sets up loggers basically.
fileConfig(config.config_file_name)

# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
from models.datamodel1 import Model1
from models.datamodel2 import Model2, Model1Model2
from models.base import Base
target_metadata = Base.metadata

# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.


def run_migrations_offline():
    """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"},
        include_schemas=True,
    )

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


def run_migrations_online():
    """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,
            include_schemas=True
        )

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


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

至于 alembic.ini 文件我没有做任何更改,我只是评论了这一行:

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

因为我在migrations/env.py

中赋值

当我进行更改并且 运行 alembic revision --autogenerate -m 'Add new updates' 迁移文件正确生成并且一切正常时。
但是当我 运行 alembic revision --autogenerate -m 'Add new updates' 没有任何变化时,它在终端中显示:

INFO  [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO  [alembic.runtime.migration] Will assume transactional DDL.
INFO  [alembic.ddl.postgresql] Detected sequence named 'model2_table_model2_id_seq' as owned by integer column 'model2_table(model2_id)', assuming SERIAL and omitting
INFO  [alembic.ddl.postgresql] Detected sequence named 'model1_model2_table_id_seq' as owned by integer column 'model1_model2_table(id)', assuming SERIAL and omitting
  Generating /home/user/projects/dev/project/migrations/versions/45c6fbdbd23c_add_new_updates.py ...  done

并生成包含以下内容的空迁移文件:

"""Add new updates

Revision ID: 45c6fbdbd23c
Revises: 5c17014a7c18
Create Date: 2021-12-27 17:11:13.964287

"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '45c6fbdbd23c'
down_revision = '5c17014a7c18'
branch_labels = None
depends_on = None


def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    pass
    # ### end Alembic commands ###


def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    pass
    # ### end Alembic commands ###

这是预期的行为还是与我的架构有关?

如何防止 Alembic 在没有更改时生成那些空的迁移文件?

Is this the expected behavior or it has something to do with my architecture?

这是预期的行为。命令 alembic revision --autogenerate 总是创建一个新的迁移文件。如果不存在任何更改,那么它会创建一个空的。

您可以使用 alembic-autogen-check 检查您的迁移是否与模型同步。

~ $ alembic-autogen-check
INFO  [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO  [alembic.runtime.migration] Will assume transactional DDL.
INFO: Migrations in sync.

How to prevent Alembic from generating those empty migration files when there are no changes?

此外 alembic-autogen-check returns 零代码仅在迁移中与模型同步。所以,你可以将它作为一个命令使用

alembic-autogen-check || alembic revision --autogenerate -m 'Add new updates'

不过单独使用好像不太方便