sqlalchemy中需用方言特有类型(如postgresql.jsonb)声明字段,否则退化为text;alembic autogenerate不识别扩展约束和索引,须手动配置;django混用jsonfield路径会导致迁移静默失败,且复杂查询需额外建索引。
不能直接从模型正向生成带高级扩展类型的表——orm 层和数据库方言之间存在语义断层,硬塞会丢功能或报错。
SQLAlchemy 中 ARRAY、JSONB、HSTORE 为什么建表后字段类型不对
PostgreSQL 的 ARRAY 或 JSONB 在 SQLAlchemy 里得靠 postgresql.ARRAY、postgresql.JSONB 显式声明,用通用类型如 types.JSON 或 types.ARRAY 会退化成 TEXT 或 BYTEA,迁移时看着建成了,查 \d table_name 才发现不是原生类型。
- 必须导入对应方言模块:
from sqlalchemy.dialects.postgresql import JSONB, ARRAY - 模型字段写法:
metadata = Column(JSONB),不是Column(types.JSON) - 如果用了
AutoIncrement+UUID组合,server_default得配text("gen_random_uuid()"),否则 Alembic 生成的 SQL 缺函数依赖
Alembic revision --autogenerate 为什么漏掉自定义类型或约束
Alembic 默认只识别 SQLAlchemy 原生支持的类型和约束,像 ExclusionConstraint、postgresql.BTREE 索引、或者带 WITH (fillfactor=70) 的选项,autogenerate 看不见,也不会 diff 进迁移脚本。
- 手动在
env.py的include_object回调里放行特定对象:if obj.name == "my_exclusion_index": return True - 复杂索引或约束建议放弃 autogenerate,改用
alembic revision --empty后手写op.create_index(..., postgresql_using="btree", postgresql_ops={...}) - 检查
context.get_context().dialect.name == "postgresql"再分支处理,避免 SQLite 测试环境崩掉
Django 的 ArrayField 和 JSONField 在 migrate 时被忽略
Django 4.2+ 默认用数据库原生 JSONField,但如果你在旧项目里混用了 django.contrib.postgres.fields.JSONField 和新内置字段,migrate 会静默跳过——因为 Django 认为“类型没变”,实际底层 SQL 类型可能从 TEXT 变成 JSONB,但 migration 不触发。
- 确认字段导入路径:新项目统一用
models.JSONField,别留postgres.fields - 强制重建字段:加个临时字段 →
RunPython搬数据 → 删旧字段 → rename,比赌 autodetect 可靠 -
ArrayField的base_field必须是可比较类型(比如不能是JSONField套ArrayField),否则makemigrations直接报ValueError: Cannot resolve field
最麻烦的不是写错类型,而是类型写对了、迁移跑过了、应用也连上了,结果某个查询突然走不了索引——因为 JSONB 字段上没建 jsonb_path_ops 索引,或者 ARRAY 查询用了 @> 却忘了开 GIN。这些不会在建表时报错,得等压测才暴露。










