改了模型,数据库为什么能自动跟上?——Django 迁移(migrations)的底层

改了模型,数据库为什么能自动跟上?------Django 迁移(migrations)的底层

这篇文章讲 Django migrations 的工作原理:makemigrations/migrate 做了什么、迁移文件的结构、依赖与状态表,以及数据迁移和冲突合并------这是团队协作中避不开的一块。

一次"改了模型,数据库却没变"的翻车

刚学 Django 时,我在模型里加了一个字段,兴冲冲地重启服务,结果一查数据库------报错:"Unknown column 'xxx'"。我这才知道:改了模型,数据库不会自动跟着变,中间还差一步叫"迁移"。

后来我才搞懂,Django 的 migrations 是一套相当精巧的"数据库版本控制"机制:它把你的模型改动,记录成一个个可追溯、可回滚、可合并的"补丁",让数据库 schema 和模型定义始终同步。这篇文章就把它的底层拆开。

为什么需要迁移:模型和数据库是两回事

Django 里,你的数据表结构定义在 models.py(Python 代码),但真正的表结构存在于数据库里。两者不会自动同步------你需要一个"翻译官",把 Python 模型的改动,翻译成数据库能执行的 SQL。

这个"翻译官"就是 migrations。它的工作分两步:

bash 复制代码
python manage.py makemigrations   # ① 对比模型和已有迁移,生成"迁移文件"
python manage.py migrate          # ② 执行迁移文件,真正改动数据库

makemigrations 只生成文件、不改数据库 ;migrate 才真正动手改库。记住这个分工,能少踩很多坑。

迁移文件里装了什么

makemigrations 会在应用的 migrations/ 目录下生成形如 0002_add_age_field.py 的文件,里面是一个 Migration 类:

python 复制代码
from django.db import migrations, models

class Migration(migrations.Migration):
    dependencies = [("myapp", "0001_initial")]   # 依赖哪个迁移

    operations = [
        migrations.AddField(
            model_name="user",
            name="age",
            field=models.IntegerField(default=0),
        ),
    ]

两个关键部分:

  • dependencies :这个迁移建立在哪个迁移之上。它决定了迁移的执行顺序,也构成了"迁移链"。
  • operations:要做的具体操作,每个 operation 对应一条或多条 SQL。常见的有:
text 复制代码
CreateModel / DeleteModel   建表 / 删表
AddField / RemoveField      加字段 / 删字段
AlterField                  改字段(类型、默认值等)
RenameField / RenameModel   重命名
RunPython                   执行自定义 Python(做"数据迁移")

理解 dependencies 是理解迁移的关键 :迁移不是"快照",而是一条有先后顺序的补丁链,每个补丁都声明"我在谁之后"。

migrate 怎么知道"执行到哪了"

Django 在数据库里维护一张特殊表 django_migrations,记录"哪些迁移已经执行过了":

text 复制代码
id | app      | name          | applied
1  | myapp    | 0001_initial  | 2024-01-01 10:00:00
2  | myapp    | 0002_add_age  | 2024-01-02 11:00:00

migrate 会对比"磁盘上的迁移文件"和"这张表",只执行那些还没被记录的新迁移。这也解释了为什么迁移能"断点续传"------它靠这张表记住进度。

数据迁移:不光改表结构,还能改数据

schema 迁移改的是"表结构",但有时你还需要改已有数据 。比如把原来的 name 字段拆成 first_name 和 last_name,光加字段不够,还得把存量数据拆开填进去。这时用 RunPython:

python 复制代码
def split_names(apps, schema_editor):
    User = apps.get_model("myapp", "User")
    for user in User.objects.all():
        first, _, last = user.name.partition(" ")
        user.first_name = first
        user.last_name = last
        user.save()

class Migration(migrations.Migration):
    operations = [
        migrations.RunPython(split_names),   # 纯 Python,写自己的逻辑
    ]

注意要用 apps.get_model() 拿到历史版本 的模型,而不是直接 from .models import User------因为迁移执行时,模型的代码可能已经又变了。

团队协作的两大坑:冲突与合并

多人开发时,迁移最容易出问题:

  1. 迁移冲突 :两个人都从 0001 生成了各自的 0002,合并代码后出现两个"都依赖 0001 的 0002"。解法是用 python manage.py makemigrations --merge 自动生成一个合并迁移,或手动调整 dependencies。
  2. 手改数据库 :有人图省事直接改了数据库,导致 django_migrations 表和实际 schema 不一致,后续迁移全线报错。铁律:永远用迁移改库,不要手改。

几条保命实践

  1. 迁移文件纳入版本控制,和代码一起提交、一起评审;
  2. 每次改模型,makemigrations + migrate 两步都做,别只做一半;
  3. 上线前在本地跑一遍迁移 ,尤其是数据迁移(RunPython),确认它可回滚(migrate myapp 0001 能退回去);
  4. 迁移要小步走:一个大迁移不如几个语义清晰的小迁移,出问题好定位。

收个尾

回到开头那个"改了模型数据库却不变"的翻车:那一步之差,就是 makemigrations 和 migrate。Django 的迁移机制,本质是一套**"模型改动 → 补丁文件 → 依赖链 → 进度表"的版本控制系统**------它让数据库 schema 的演进变得可追溯、可回滚、可协作。理解这四点,你就真正会"迁移"了。

Django 迁移建立在扎实的 Python 与 ORM 功底之上,想系统掌握 Django 与 Python,推荐 B站【408实验室】的《Python 完全自学教程》。

相关推荐
编码追梦人1 小时前
基于 ESP32-S3 的便携式多参数环境监护仪设计:从硬件选型到 FreeRTOS 多任务落地
数据库·单片机·嵌入式硬件·mongodb·esp32·freertos·低功耗设计
sunburn-1 小时前
数据库增删改查:从 SQL 基础到实践要点
java·数据库·mysql
誰能久伴不乏4 小时前
桌面客户端的运行日志系统:SQLite 分块 + 独立写线程 + 文本降级
数据库·c++·qt·架构·sqlite
程序员清风11 小时前
数据处理脚本工程化:日志、配置、命令行与测试
服务器·开发语言·数据库
正在走向自律12 小时前
数据集成产品的技术底座:从日志解析原理到 KFS 同步链路搭建(含建表与增删改查完整代码)
数据库·oracle·信创·数据集成·国产数据库·kingbasees·全量+增量同步
程序员塔塔喵12 小时前
【MySQL篇|第七篇】MySQL事务
数据库·mysql·mysql事务
独隅13 小时前
Claude API 实战教程:从入门到完整项目
android·java·数据库·claude·claude code
_孤傲_13 小时前
快速掌握 MySQL 语法
数据库·后端·mysql
数字新视界15 小时前
一体化机柜在数据中心运维中的创新应用与优势分析
大数据·服务器·数据库·动环监控系统·动力与环境监控系统