Django 迁移文件是否需要同步到服务器

Django 迁移文件是否需要同步到服务器

必须同步迁移文件!

为什么不可以不同步迁移文件。直接在服务器上执行python manage.py makemigration生成迁移脚本,然后再运行python manage.py migrate进行迁移。

为什么不建议在服务器上执行 makemigrations

场景还原

假设有一个博客应用,模型最初如下:

# 初始 models.py(已部署到生产环境)
class Article(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    # 生产数据库中已有 10,000 篇文章存储了这个字段
    important_data = models.CharField(max_length=100)  

开发环境的操作

当我们决定删除 important_data 字段。修改后的 models.py

class Article(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()

在开发环境生成迁移文件

python manage.py makemigrations

生成 0002_remove_article_important_data.py

# 迁移文件明确记录了"删除字段"的操作
migrations.RemoveField(
    model_name='article',
    name='important_data',
)

测试确认

  • 开发数据库执行迁移 → 字段被安全删除
  • 代码提交到 Git(包含此迁移文件)

生产环境的危险操作

错误做法:没有同步迁移文件,直接在服务器执行:

python manage.py makemigrations  # 危险!
python manage.py migrate

graphic

可能的结果

  1. 由于生产数据库仍存在 important_data 字段,而 models.py 已删除该字段,Django 会困惑于这种不一致状态

  2. 可能生成完全不同的迁移文件,例如:

    # 自动生成的迁移可能尝试"修复"不一致
    migrations.AlterField(
        model_name='article',
        name='important_data',
        field=models.CharField(max_length=100, null=True)
    )
    
  3. 执行迁移后:

    -
    字段被改为可空,但数据仍然存在(暂时安全)

    • 下次部署时若再次误操作,可能导致数据被清空

为什么 Django 在开发和生产环境对迁移的处理不同?

根本原因在于 Django 对“模型-数据库一致性”的假设不同,导致它在不同环境下对迁移的生成逻辑有本质区别

开发环境:Django 认为你是“主动”修改模型

假设前提

  • 开发环境:Django 默认你正在主动修改代码models.py),并希望数据库跟随变更。
  • 迁移文件 是你的明确意图记录,Django 不会质疑你的决定。

行为表现

  • 当你运行 python manage.py makemigrations
    • Django 对比 当前 models.py上一次迁移文件,计算差异。
    • 生成一个 明确的变更指令(如 AddFieldRemoveField)。
    • 不管数据库当前是否有数据,它都会按照你的代码生成迁移。
      • 如果有数据,migrate 时会警告你(但依然执行)。
      • 如果没数据,直接静默执行。

关键点

  • Django 信任你是主动修改,所以生成的迁移是“确定性的”(每次相同代码会生成相同迁移)。
  • 迁移文件是“指令”,不是“猜测”。

生产环境:Django 认为“数据库应该和代码一致”

假设前提

  • 生产环境:Django 默认 数据库应该和 models.py 完全一致
  • 如果发现不一致(比如数据库有字段但代码没有),它会认为:
    • 可能是 代码没同步(比如 git pull 漏了迁移文件)。
    • 可能是 数据库被手动修改(比如有人直接 ALTER TABLE)。
    • 它不会假设你是“主动”删除字段,而是认为“出现了意外的不一致”。

行为表现

  • 当你运行 python manage.py makemigrations(生产环境):
    • Django 发现 数据库有字段 important_data,但 models.py 没有
    • 它不会直接生成 RemoveField,而是尝试“修复不一致”:
      • 可能生成 AlterField(让字段可空,避免数据丢失)。
      • 可能生成 SeparateDatabaseAndState(假装删除了字段,但实际没动数据库)。
      • 甚至可能生成 AddField(反向操作,导致混乱)。

关键风险

  • Django 在“猜测”你的意图,而不是执行你的明确指令。
  • 生成的迁移可能不符合你的真实需求,甚至导致数据丢失或逻

现实场景对比

场景:删除 important_data 字段

环境 执行 makemigrations 生成的迁移逻辑 风险
开发环境 RemoveField(明确删除) 直接删除字段,有数据会警告 可控,你知道自己在做什么
生产环境 AlterField(null=True)(尝试修复) 可能没真正删除字段,但代码以为删了 数据残留,后续代码可能出错

Django如何判断环境

Django 如何“间接”判断环境?

Django 主要通过以下方式 间接 区分开发和生产环境:

判断依据 开发环境(Development) 生产环境(Production)
DEBUG = True ✅ 通常开启(显示详细错误) ❌ 必须关闭(安全风险)
ALLOWED_HOSTS 可能是 ['*']['localhost'] 必须配置真实域名/IP
数据库配置 可能用 SQLite(本地开发) PostgreSQL/MySQL(生产)
静态文件处理 runserver 自动处理 需要 Nginx/CDN 托管
SECRET_KEY 可能硬编码或默认值 必须从环境变量读取

如何显式告诉 Django 当前环境?

settings.py 中区分环境

import os

# 通过环境变量判断
ENV = os.getenv("DJANGO_ENV", "development")  # 默认开发环境

if ENV == "production":
    DEBUG = False
    ALLOWED_HOSTS = ["example.com"]
    # 生产数据库配置
else:
    DEBUG = True
    ALLOWED_HOSTS = ["*"]
    # 开发数据库配置

使用 .env 文件(推荐)

# .env
DJANGO_ENV=production
SECRET_KEY=your_prod_key

然后在 settings.py 中读取:

from dotenv import load_dotenv
load_dotenv()  # 加载 .env 文件

DEBUG = os.getenv("DJANGO_ENV") != "production"

正确的工作流

开发环境生成迁移文件

# 本地修改 models.py 后
python manage.py makemigrations  # 生成迁移文件
python manage.py migrate        # 本地测试
git add blog/migrations/0002_auto_20240520.py  # 提交新迁移文件
git commit -m "Add new_field to Blog model"

服务器同步后执行迁移

git pull origin main  # 获取最新代码(含迁移文件)
python manage.py migrate  # 仅执行迁移,不生成新文件

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1909773034@qq.com

×

喜欢就点赞,疼爱就打赏