一个迁移文件里到底有什么?自动生成能信几成?数据迁移怎么写?想先看 SQL 再执行怎么办?这篇每种改动都从同一个基线项目出发,用真实的 flask db 命令逐个验证。
文件名: r001_create_user_table.py (-m "create user table" 变成文件名里的小写下划线;--rev-id r001 指定了 revision id)
文件头部:
"""create user table
Revision ID: r001
Revises:
Create Date: 2026-10-02 12:30:38.222633
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'r001'
down_revision = None
branch_labels = None
depends_on = None
文件里的函数: ['upgrade', 'downgrade']上一篇:SQLAlchemy 查询日志;下一篇:数据迁移回滚。
[加一列 age] 检测到: Detected added column 'user.age'
[改列长度 name 50→80] 检测到: Detected type change from VARCHAR(length=50) to String(length=80) on 'user.name'
[改 nullable(email 不可空)] 检测到: Detected NOT NULL on column 'user.email'
[改 server_default 的值] 没有检测到任何变化(没有生成迁移)
[给 email 加索引] 检测到: Detected added index 'ix_user_email' on '('email',)'
[给 email 加 unique=True] 检测到: Detected added unique constraint None on '('email',)'
[把 email 改名为 mail] 检测到: Detected added column 'user.mail'; Detected removed column 'user.email'
[删掉 email 列] 检测到: Detected removed column 'user.email'第 74 篇看过 init、migrate、upgrade 的基本流程,这一篇看文件内容与检测边界。
生成的迁移里的操作: ["batch_op.create_unique_constraint(None, ['email'])"] upgrade → 失败:ValueError: Constraint must have a name
空脚本里原来是 pass;手写的两个函数:
def upgrade():
op.execute("update user set email = name || '@example.com' where email = 'none'")
def downgrade():
op.execute("update user set email = 'none' where email like '%@example.com'")
执行前: [('bob', 'none')]
upgrade 后: [('bob', 'bob@example.com')] | 退出码 0
downgrade 后: [('bob', 'none')] | 退出码 0 upgrade r001:r900 --sql 的输出:
-- Running upgrade r001 -> r900
update user set email = name || '@example.com' where email = 'none';
UPDATE alembic_version SET version_num='r900' WHERE alembic_version.version_num = 'r001';
数据库的版本号前后: [('r001',)] → [('r001',)] (没变) stamp r777 之后 alembic_version = [('r777',)] (没有执行 r777 的任何操作,只是记账)
flask db current: ['r777 (head)']