bigywater.tech
Django · 51

makemigrations 如何检测模型变化

makemigrations 是怎么知道你改了什么的?为什么改个 choices 也要生成迁移?改名为什么会问你一句「是不是改名」?这篇一次只改一处,把 14 种改动各跑一遍。

0 条 SQL
只改 help_text / choices 也会生成 Alter field 迁移,但 sqlmigrate 是空的
改名
--noinput 下字段改名 = 删除 + 新增(数据丢失);改名同时改类型,永远是删除 + 新增
纯 Python
新增方法 / @property 不产生迁移

14 种改动的检测结果

实测 · 本机执行输出
  改动                                     检测到的操作                                               SQL 条数
  新增字段(有 default)                        Add field stock to item                              4
  新增字段(null=True)                        Add field stock to item                              1
  新增字段(没有 default 也不许 NULL)              Field 'stock' on model 'item' not migrated: it is im -
  max_length 20 → 50                     Alter field name on item                             4
  字段类型 IntegerField → BigIntegerField    Alter field price on item                            4
  只改 help_text(不影响数据库)                   Alter field price on item                            0
  只改 choices(不影响数据库)                     Alter field name on item                             0
  删除字段                                   Remove field note from item                          1
  字段改名 price → cost                      Remove field price from item; Add field cost to item 5
  模型类改名 Item → Product                   Create model Product; Delete model Item              2
  新增 Meta.indexes                        Create index lab_item_name_1e8c73_idx on field(s) na 1
  新增 unique_together                     Alter unique_together for item (1 constraint(s))     1
  新增方法 / @property(纯 Python)             (没有检测到变化)                                            -
  改 Meta.ordering                        Change Meta options on item                          0

比较的对象是「所有迁移文件按顺序重放得到的状态」与「当前 models」——不是数据库。所以数据库被手工改过的部分它不知道(第 56 篇)。SQL 条数是 SQLite 的(它改列要重建表);PostgreSQL 上同样的改动通常更少。

改名的歧义

实测 · 本机执行输出
  --noinput: ['- Remove field price from item', '+ Add field cost to item']
  交互回答 y: ['~ Rename field price on item to cost'] | 提问原文: ["Was item.price renamed to item.cost (a IntegerField)? [y/N] Migrations for 'lab':"]
实测 · 本机执行输出
   ['- Remove field price from item', '+ Add field cost to item']

Django 无法区分「改名」和「删了一个再加一个同类型的」,于是交互式地问你。CI 里用 --noinput 或 --check 时它按「删除 + 新增」处理,这是数据丢失的来源。改名建议分两次提交:先改名(回答 y),再改类型。

分步演示:makemigrations 检测什么

makemigrations 检测
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Python 3.14.7 / Django 6.1.1(默认用 SQLite,其它数据库会在文中注明),用 django.test.Client 或真实的本机服务器进程实测;计时用 time.perf_counter,数字每次运行会有小幅抖动。SQLite;每个改动都在全新的临时项目里做;--dry-run 看检测,随后真正生成迁移并 sqlmigrate 数 SQL 条数(不含 BEGIN / COMMIT)
    • 没有验证:PostgreSQL 上每种改动的 SQL 条数、外键 / 多对多 / 自定义字段的检测、约束(CheckConstraint / UniqueConstraint)的检测、swappable 模型、我第一次跑时因为 .pyc 时间戳缓存漏检了 max_length 一项(已用 PYTHONDONTWRITEBYTECODE 排除)
    京ICP备2026067502号-1京公网安备11010502063487号