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

sin*_*ium 3 python postgresql sqlalchemy alembic

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

\n
.\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 alembic.ini\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 main.py\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 migrations\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 env.py\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 README\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 script.py.mako\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 versions\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 models\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 base.py\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 datamodel1.py\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 datamodel2.py\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 __init__.py\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 requirements.txt\n\n3 directories, 10 files\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
from sqlalchemy.ext.declarative.api import declarative_base, DeclarativeMeta\n\nBase: DeclarativeMeta = declarative_base()\n
Run Code Online (Sandbox Code Playgroud)\n

的内容models/datamodel1.py是:

\n
from models.base import Base\nfrom sqlalchemy.sql.schema import Column\nfrom sqlalchemy.sql.sqltypes import String, Date, Float\n\n\nclass Model1(Base):\n    __tablename__ = 'model1_table'\n\n    model1_id = Column(String, primary_key=True)\n    col1 = Column(String)\n    col2 = Column(String)\n
Run Code Online (Sandbox Code Playgroud)\n

的内容models/datamodel2.py是:

\n
from models.base import Base\nfrom sqlalchemy.orm import relationship\nfrom sqlalchemy.sql.sqltypes import String, Integer, Date\nfrom sqlalchemy.sql.schema import Column, ForeignKey\n\n\n# The many to may relationship table\nclass Model1Model2(Base):\n    __tablename__ = 'model1_model2_table'\n\n    id = Column(Integer, primary_key=True)\n    model_1_id = Column(String, ForeignKey('model1.model1_id'))\n    model_2_id = Column(Integer, ForeignKey('model2.model2_id'))\n\n\nclass Model2(Base):\n    __tablename__ = 'model2_table'\n\n    model2_id = Column(Integer, primary_key=True)\n    model2_col1 = Column(String)\n    model2_col2 = Column(Date)\n    # Many to many relationship\n    model1_model2 = relationship('Model1', secondary='model1_model2_table', backref='model1_table')\n
Run Code Online (Sandbox Code Playgroud)\n

的内容migrations/env.py是:

\n
from logging.config import fileConfig\n\nfrom sqlalchemy import engine_from_config\nfrom sqlalchemy import pool\n\nfrom alembic import context\nimport sys\nsys.path.append('./')\n\n\n\n# this is the Alembic Config object, which provides\n# access to the values within the .ini file in use.\n\nconfig = context.config\n\n# I added the following 2 lines to replace the sqlalchemy.url in alembic.ini file.  \ndb_string = f'postgresql+psycopg2://{db_username}:{db_password}@{db_host}:{db_port}/{db_name}'\nconfig.set_main_option('sqlalchemy.url', db_string)\n\n# Interpret the config file for Python logging.\n# This line sets up loggers basically.\nfileConfig(config.config_file_name)\n\n# add your model's MetaData object here\n# for 'autogenerate' support\n# from myapp import mymodel\n# target_metadata = mymodel.Base.metadata\nfrom models.datamodel1 import Model1\nfrom models.datamodel2 import Model2, Model1Model2\nfrom models.base import Base\ntarget_metadata = Base.metadata\n\n# other values from the config, defined by the needs of env.py,\n# can be acquired:\n# my_important_option = config.get_main_option("my_important_option")\n# ... etc.\n\n\ndef run_migrations_offline():\n    """Run migrations in 'offline' mode.\n\n    This configures the context with just a URL\n    and not an Engine, though an Engine is acceptable\n    here as well.  By skipping the Engine creation\n    we don't even need a DBAPI to be available.\n\n    Calls to context.execute() here emit the given string to the\n    script output.\n\n    """\n    url = config.get_main_option("sqlalchemy.url")\n    context.configure(\n        url=url,\n        target_metadata=target_metadata,\n        literal_binds=True,\n        dialect_opts={"paramstyle": "named"},\n        include_schemas=True,\n    )\n\n    with context.begin_transaction():\n        context.run_migrations()\n\n\ndef run_migrations_online():\n    """Run migrations in 'online' mode.\n\n    In this scenario we need to create an Engine\n    and associate a connection with the context.\n\n    """\n    connectable = engine_from_config(\n        config.get_section(config.config_ini_section),\n        prefix="sqlalchemy.",\n        poolclass=pool.NullPool,\n    )\n\n    with connectable.connect() as connection:\n        context.configure(\n            connection=connection,\n            target_metadata=target_metadata,\n            include_schemas=True\n        )\n\n        with context.begin_transaction():\n            context.run_migrations()\n\n\nif context.is_offline_mode():\n    run_migrations_offline()\nelse:\n    run_migrations_online()\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
sqlalchemy.url = driver://user:pass@localhost/dbname\n
Run Code Online (Sandbox Code Playgroud)\n

因为我将它分配给migrations/env.py

\n

当我进行更改并运行时,alembic revision --autogenerate -m 'Add new updates'迁移文件会正确生成,并且一切都会按预期进行。
\n但是当我alembic revision --autogenerate -m 'Add new updates'在没有更改的情况下运行时,它会在终端中显示:

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

它会生成空的迁移文件,其中包含:

\n
"""Add new updates\n\nRevision ID: 45c6fbdbd23c\nRevises: 5c17014a7c18\nCreate Date: 2021-12-27 17:11:13.964287\n\n"""\nfrom alembic import op\nimport sqlalchemy as sa\n\n\n# revision identifiers, used by Alembic.\nrevision = '45c6fbdbd23c'\ndown_revision = '5c17014a7c18'\nbranch_labels = None\ndepends_on = None\n\n\ndef upgrade():\n    # ### commands auto generated by Alembic - please adjust! ###\n    pass\n    # ### end Alembic commands ###\n\n\ndef downgrade():\n    # ### commands auto generated by Alembic - please adjust! ###\n    pass\n    # ### end Alembic commands ###\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

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

\n

Eug*_*nij 7

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

这是预期的行为。命令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.
Run Code Online (Sandbox Code Playgroud)

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

alembic-autogen-check仅在迁移与模型同步时才返回零代码。因此,您可以将其用作一个命令

alembic-autogen-check || alembic revision --autogenerate -m 'Add new updates'
Run Code Online (Sandbox Code Playgroud)

但似乎不如单独使用方便