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
可能的结果:
由于生产数据库仍存在
important_data
字段,而models.py
已删除该字段,Django 会困惑于这种不一致状态可能生成完全不同的迁移文件,例如:
# 自动生成的迁移可能尝试"修复"不一致 migrations.AlterField( model_name='article', name='important_data', field=models.CharField(max_length=100, null=True) )
执行迁移后:
-
字段被改为可空,但数据仍然存在(暂时安全)- 下次部署时若再次误操作,可能导致数据被清空
为什么 Django 在开发和生产环境对迁移的处理不同?
根本原因在于 Django 对“模型-数据库一致性”的假设不同,导致它在不同环境下对迁移的生成逻辑有本质区别
开发环境:Django 认为你是“主动”修改模型
假设前提
- 开发环境:Django 默认你正在主动修改代码(
models.py
),并希望数据库跟随变更。 - 迁移文件 是你的明确意图记录,Django 不会质疑你的决定。
行为表现
- 当你运行
python manage.py makemigrations
:- Django 对比 当前
models.py
和 上一次迁移文件,计算差异。 - 生成一个 明确的变更指令(如
AddField
、RemoveField
)。 - 不管数据库当前是否有数据,它都会按照你的代码生成迁移。
- 如果有数据,
migrate
时会警告你(但依然执行)。 - 如果没数据,直接静默执行。
- 如果有数据,
- Django 对比 当前
关键点:
- Django 信任你是主动修改,所以生成的迁移是“确定性的”(每次相同代码会生成相同迁移)。
- 迁移文件是“指令”,不是“猜测”。
生产环境:Django 认为“数据库应该和代码一致”
假设前提
- 生产环境:Django 默认 数据库应该和
models.py
完全一致。 - 如果发现不一致(比如数据库有字段但代码没有),它会认为:
- 可能是 代码没同步(比如
git pull
漏了迁移文件)。 - 可能是 数据库被手动修改(比如有人直接
ALTER TABLE
)。 - 它不会假设你是“主动”删除字段,而是认为“出现了意外的不一致”。
- 可能是 代码没同步(比如
行为表现
- 当你运行
python manage.py makemigrations
(生产环境):- Django 发现 数据库有字段
important_data
,但models.py
没有。 - 它不会直接生成
RemoveField
,而是尝试“修复不一致”:- 可能生成
AlterField
(让字段可空,避免数据丢失)。 - 可能生成
SeparateDatabaseAndState
(假装删除了字段,但实际没动数据库)。 - 甚至可能生成
AddField
(反向操作,导致混乱)。
- 可能生成
- Django 发现 数据库有字段
关键风险:
- 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