django多环境配置核心是通过django_settings_module环境变量动态导入settings模块,而非在settings.py中条件判断;应分离base、dev、prod配置,敏感信息如secret_key必须从环境变量读取,禁止硬编码。

用 os.environ 读取环境变量决定加载哪个 settings 模块
Django 本身不内置多环境配置机制,核心思路是让 manage.py 和 wsgi.py 根据环境变量动态导入不同 settings 模块。关键不是改 Django,而是控制 Python 的模块导入逻辑。
常见错误是直接在 settings.py 里写 if os.environ.get('ENV') == 'prod' —— 这会让所有环境都加载全部配置,敏感信息(如密钥)可能意外暴露或被静态检查误报。
- 在项目根目录建
settings/目录,下设__init__.py、base.py、dev.py、prod.py -
base.py放通用配置(INSTALLED_APPS、中间件骨架、数据库字段默认值等) -
dev.py和prod.py分别from .base import *再覆盖关键项(DEBUG、SECRET_KEY、DATABASES、日志级别) - 启动时通过
export DJANGO_SETTINGS_MODULE=myproject.settings.prod显式指定模块路径
为什么不能把 SECRET_KEY 写死在 prod.py 里
硬编码密钥等于把生产环境大门钥匙刻在门框上。哪怕加了 gitignore,也挡不住本地误提交、CI 日志泄露、或运维人员直接 cat 文件。
正确做法是让 prod.py 从环境变量读取:
import os SECRET_KEY = os.environ['DJANGO_SECRET_KEY'] # 不要用 get(),缺了就该炸
这样做的实际好处:
- 本地开发用
dev.py可设固定值(SECRET_KEY = 'dev-only'),避免每次 runserver 都报密钥弱警告 - 生产环境由容器编排工具(如 Docker Compose 的
environment:或 Kubernetes Secret)注入,和代码完全隔离 - CI 流水线可安全地用加密变量传入,无需修改任何 Python 文件
DEBUG=False 时静态文件 404 或白屏的典型原因
很多人切到生产环境后发现 CSS/JS 加载失败、Admin 页面空白,第一反应是 Nginx 配置错了——其实常是 settings.prod.py 漏了关键配置。
Django 在 DEBUG=False 下默认不提供静态文件服务,且对 ALLOWED_HOSTS 校验极其严格:
-
ALLOWED_HOSTS必须显式列出域名(如['myapp.com', 'www.myapp.com']),['*']在生产环境会被拒绝(除非配合SECURE_PROXY_SSL_HEADER) - 静态文件路径必须设
STATIC_ROOT = '/var/www/myapp/static/',然后运行python manage.py collectstatic --noinput打包到该目录 - 确保
STATIC_URL = '/static/'与 Nginx 的location /static/路径一致,注意末尾斜杠是否匹配 - 如果用了 CDN,
STATIC_URL应设为完整 URL(如https://cdn.example.com/static/)
用 django-environ 解析数据库 URL 时的坑
直接写 DATABASES = {'default': {...}} 容易出错,尤其当数据库地址来自 Heroku 的 DATABASE_URL 环境变量时。用 django-environ 是更稳的选择,但要注意版本兼容性。
安装后,在 prod.py 中:
import environ
env = environ.Env()
DATABASES = {
'default': env.db(),
}
这时容易忽略三点:
-
env.db()默认读取DATABASE_URL,但如果环境变量名是DB_URL,得写env.db('DB_URL') - PostgreSQL 的 URL 若含特殊字符(如密码里的
@或/),必须 URL 编码,否则解析失败并静默回退到 SQLite - Django 4.2+ 对
env.db()返回的CONN_MAX_AGE默认值更保守,如需连接池,显式加'CONN_MAX_AGE': 60到env.db()的返回字典中
最麻烦的是错误不报——URL 解析失败时 django-environ 会悄悄 fallback 到 SQLite,导致你上线后才发现数据全写进本地 db.sqlite3 了。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











