这个案例能帮你理解Django的迁移系统是如何管理数据库变化的吗

wen 全栈框架 4

本文目录导读:

  1. 案例:构建一个简单的博客应用
  2. 为什么这对你理解Django很重要?

是的,Django 的迁移系统确实是一个非常强大且设计精良的工具,用于管理数据库模式(schema)的变更,通过一个具体的案例,可以很好地理解它的核心概念和工作流程。

下面我为你构建一个典型的“博客应用”开发案例,并逐步演示 Django 如何通过迁移系统来管理数据库变化。


案例:构建一个简单的博客应用

目标: 开发一个博客应用,需要存储作者和文章信息。

第一阶段:初始模型与迁移

  1. 定义模型 (models.py)

    # blog/models.py
    from django.db import models
    class Author(models.Model):
        name = models.CharField(max_length=100)
        email = models.EmailField()
        def __str__(self):
            return self.name
    class Article(models.Model):
        title = models.CharField(max_length=200)
        content = models.TextField()
        pub_date = models.DateTimeField('date published')
        author = models.ForeignKey(Author, on_delete=models.CASCADE)
        def __str__(self):
            return self.title
  2. 生成迁移文件

    运行命令:

    python manage.py makemigrations blog

    发生了什么? Django 会扫描 blog/models.py 文件,与 blog/migrations/ 目录下已有的迁移文件进行比较,由于这是第一次,它会检测到 AuthorArticle 这两个新模型,并自动创建一个迁移文件(0001_initial.py)。

    迁移文件内部(简化版)

    # blog/migrations/0001_initial.py
    from django.db import migrations, models
    class Migration(migrations.Migration):
        initial = True
        dependencies = []
        operations = [
            migrations.CreateModel(
                name='Author',
                fields=[
                    ('id', models.AutoField(...)),
                    ('name', models.CharField(max_length=100)),
                    ('email', models.EmailField()),
                ],
            ),
            migrations.CreateModel(
                name='Article',
                fields=[
                    ('id', models.AutoField(...)),
                    ('title', models.CharField(max_length=200)),
                    ('content', models.TextField()),
                    ('pub_date', models.DateTimeField(...)),
                    ('author', models.ForeignKey(to='blog.Author', ...)),
                ],
            ),
        ]
  3. 应用迁移到数据库

    运行命令:

    python manage.py migrate blog

    发生了什么? Django 会执行 0001_initial.py 中的操作,在数据库中创建 blog_authorblog_article 两张表,包括所有字段、约束、索引和外键关系,Django 会记录本次迁移的执行状态(在 django_migrations 表中插入一条记录)。


第二阶段:需求变更,修改模型

新需求: 文章增加一个 status 字段(草稿/已发布),Article 模型需要增加一个 category(分类)字段,Author 要增加一个 bio(简介)字段。

  1. 修改模型 (models.py)

    # blog/models.py
    from django.db import models
    class Author(models.Model):
        name = models.CharField(max_length=100)
        email = models.EmailField()
        bio = models.TextField(blank=True)  # 新增字段
    class Article(models.Model):
        title = models.CharField(max_length=200)
        content = models.TextField()
        pub_date = models.DateTimeField('date published')
        author = models.ForeignKey(Author, on_delete=models.CASCADE)
        status = models.CharField(  # 新增字段
            max_length=10,
            choices=[('draft', 'Draft'), ('published', 'Published')],
            default='draft'
        )
        category = models.CharField(max_length=50, null=True) # 新增字段,允许为空
  2. 再次生成迁移文件

    运行命令:

    python manage.py makemigrations blog

    发生了什么? Django 再次比较当前模型与 0001_initial.py 的差异,它会发现:

    • Author 模型多了一个 bio 字段。
    • Article 模型多了 statuscategory 字段。
    • 自动生成一个新的迁移文件(0002_article_status_author_bio_category.py)。

    迁移文件内部(简化版)

    # blog/migrations/0002_article_status_author_bio_category.py
    from django.db import migrations, models
    class Migration(migrations.Migration):
        dependencies = [
            ('blog', '0001_initial'),  # 依赖之前的迁移
        ]
        operations = [
            migrations.AddField(
                model_name='author',
                name='bio',
                field=models.TextField(blank=True),
            ),
            migrations.AddField(
                model_name='article',
                name='status',
                field=models.CharField(
                    choices=[('draft', 'Draft'), ('published', 'Published')],
                    default='draft',
                    max_length=10
                ),
            ),
            migrations.AddField(
                model_name='article',
                name='category',
                field=models.CharField(max_length=50, null=True),
            ),
        ]
  3. 应用新的迁移

    运行命令:

    python manage.py migrate blog

    发生了什么? Django 执行 0002_... 迁移文件,在数据库的 blog_author 表中添加 bio 列(可空),在 blog_article 表中添加 statuscategory 列,如果表中有已有数据,对于 status 字段,Django 会知道设置默认值 'draft';对于 category 字段,由于允许为空,已存在的行该字段会为 NULL


第三阶段:撤销迁移

新问题: 发现 category 字段设计不合理,需要回滚。

  1. 回滚到上一个版本

    python manage.py migrate blog 0001

    发生了什么? Django 会找到从 0002 回退到 0001 所需的所有操作,并自动执行反向操作(如 RemoveField),数据库中的 biostatuscategory 列都会被删除(注意:这会丢失数据!)。


通过这个案例,我们可以清晰地看到 Django 迁移系统的关键特性:

  1. 版本控制:每个迁移文件(0001_initial.py0002_...)都是一个数据库模式的“版本快照”。
  2. 依赖链:迁移文件通过 dependencies 属性形成链条(0002 依赖于 0001),这保证了正确的执行顺序,并允许跨应用迁移。
  3. 声明式 vs 命令式:你声明模型(models.py),Django 自动分析差异并生成命令式的迁移文件(AddFieldCreateModel 等操作)。
  4. 可逆性:迁移操作通常都有对应的反向操作(CreateModel 的反向是 DeleteModelAddField 的反向是 RemoveField),这使得回滚非常可靠。
  5. 历史追踪django_migrations 表记录了每个迁移是否已被应用,防止重复执行或冲突。

为什么这对你理解Django很重要?

  • 开发协作:团队成员可以独立生成迁移,合并时进行冲突处理。
  • 数据安全:合理的迁移流程可以避免手动修改数据库导致的灾难性错误。
  • 部署自动化migrate 命令可以安全地在生产环境执行,完成数据库升级。
  • 适应变化:业务需求经常变化,强大的迁移系统让数据库模式变更变得低成本、可预测。

这个案例应该能帮助你直观地抓住迁移系统的精髓:它把数据库的变更历史,变成了与代码一样可追踪、可撤销、可协作的版本化操作序列

标签: 迁移系统

抱歉,评论功能暂时关闭!