You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Alembic反向生成升级/降级迁移问题求助(FastAPI+PostgreSQL)

问题

使用Alembic + FastAPI + PostgreSQL时,数据库为空的情况下,执行自动生成迁移脚本命令,得到的upgrade函数是删除表,downgrade函数是创建表的反向逻辑。即使手动调换代码,下次执行自动迁移还是会生成删除语句。

示例执行命令及输出

alembic revision --autogenerate -m "Testing 2"
INFO  [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO  [alembic.runtime.migration] Will assume transactional DDL.
INFO  [alembic.autogenerate.compare] Detected removed index 'ix_Site_keyword' on 'Site'
INFO  [alembic.autogenerate.compare] Detected removed table 'Site'
  Generating /app/migrations/versions/1e3a0f40182c_testing_2.py ...  done

生成的迁移文件内容

"""Testing 2

Revision ID: 1e3a0f40182c
Revises: 928ab2a61fa7
Create Date: 2024-04-04 08:32:29.784316

"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql

# revision identifiers, used by Alembic.
revision: str = '1e3a0f40182c'
down_revision: Union[str, None] = '928ab2a61fa7'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_index('ix_Site_keyword', table_name='Site')
    op.drop_table('Site')
    # ### end Alembic commands ###


def downgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('Site',
    sa.Column('id', sa.UUID(), autoincrement=False, nullable=False),
    sa.Column('title', sa.VARCHAR(), autoincrement=False, nullable=True),
    sa.Column('keyword', sa.VARCHAR(), autoincrement=False, nullable=True),
    sa.Column('description', sa.TEXT(), autoincrement=False, nullable=True),
    sa.Column('is_active', sa.BOOLEAN(), autoincrement=False, nullable=True),
    sa.Column('created_at', postgresql.TIMESTAMP(), autoincrement=False, nullable=True),
    sa.Column('updated_at', postgresql.TIMESTAMP(), autoincrement=False, nullable=True),
    sa.Column('deleted_at', postgresql.TIMESTAMP(), autoincrement=False, nullable=True),
    sa.PrimaryKeyConstraint('id', name='Site_pkey')
    )
    op.create_index('ix_Site_keyword', 'Site', ['keyword'], unique=False)
    # ### end Alembic commands ###

附env.py文件内容

from logging.config import fileConfig

from sqlalchemy import engine_from_config
from sqlalchemy import pool
from database.orm import Base
from alembic import context


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"},
    )

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



def run_migrations_online() -> None:
    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自动生成迁移的逻辑是对比当前数据库状态和target_metadata(即你的Base.metadata)的差异:

  • 你的数据库是空的,但Base.metadata里有Site表的定义
  • Alembic会误判:把数据库的空状态当成“目标状态”,把模型里的表结构当成“历史状态”,所以生成的upgrade是删除表(从有表改成无表),downgrade是创建表(从无表改成有表)
  • 另外如果alembic_version表中存在旧迁移记录,且该记录对应的操作是创建过Site表,Alembic会认为当前数据库应该有这个表,现在缺失了,进一步强化“需要删除”的错误判定。

解决步骤

  1. 初始化空数据库的迁移基线

    • 若为全新空库,先创建初始迁移并同步到数据库:
      # 若未初始化Alembic先执行(已初始化可跳过)
      alembic init migrations
      # 生成初始结构迁移
      alembic revision --autogenerate -m "initial schema"
      # 同步结构到数据库
      alembic upgrade head
      
      此时生成的upgrade会是创建表的逻辑,同时alembic_version表会记录当前正确版本。
  2. 修复现有错误迁移

    • 删除migrations/versions/下的错误迁移文件
    • 清空数据库中的alembic_version表(若有旧记录)
    • 重新执行上述初始化步骤,生成正确的初始迁移并执行。
  3. 规范后续迁移流程

    • 修改模型后,先执行alembic revision --autogenerate生成迁移脚本
    • 检查upgrade和downgrade逻辑是否符合预期(比如新增字段应在upgrade中添加,downgrade中删除)
    • 执行alembic upgrade head同步到数据库

额外注意

  • 不要手动修改迁移文件后再执行--autogenerate,Alembic会基于当前数据库和模型的差异重新生成,手动修改会被覆盖
  • 确保env.py中的target_metadata = Base.metadata正确指向你的模型基类,保证Alembic能读取到最新模型结构

内容的提问来源于stack exchange,提问作者sincorchetes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 11:16:25