bigywater.tech
Django · 12

settings 加载与 DJANGO_SETTINGS_MODULE

DJANGO_SETTINGS_MODULE 是怎么变成 settings.XXX 的?为什么在模块顶层写 X = settings.DEBUG 有时会「不跟着变」?这篇用一个真实项目,一条一条验证 settings 的惰性加载、大小写规则和覆盖行为。

惰性
import settings 不导入设置模块,第一次读取属性才导入
150
本版本 global_settings 里全大写的默认设置个数
模块级读取
override_settings 不会改变模块导入时已经读走的值

惰性加载

实测 · 本机执行输出
  [导入与读取]
    from django.conf import settings 之后,proj.settings 已导入? False
    settings._wrapped 类型: object
    读取 settings.DEBUG 之后,proj.settings 已导入? True
    settings._wrapped 类型: Settings
    settings.SETTINGS_MODULE = proj.settings

settings 是 LazySettings:导入时里面只有占位,第一次属性访问才读环境变量、导入设置模块。所以可以先 import 再决定用哪个设置模块(比如在 manage.py 里先 setdefault 环境变量)。

出错时的样子

实测 · 本机执行输出
  未设置环境变量: django.core.exceptions.ImproperlyConfigured: Requested setting DEBUG, but settings are not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call settings.configure() before accessing settings.
  DJANGO_SETTINGS_MODULE=nope.settings: ModuleNotFoundError: No module named 'nope'

没配置和配错模块名是两种不同的异常,排查时看异常类型就能分清:前者是 ImproperlyConfigured,后者是普通的 ModuleNotFoundError。

什么算设置、默认值从哪来

实测 · 本机执行输出
  [大小写与默认值]
    settings.MY_UPPER = 2
    hasattr(settings, "my_lower") = False
    settings.my_lower → AttributeError 'Settings' object has no attribute 'my_lower'
    全局默认设置个数(global_settings 里全大写的名字): 150
    proj.settings 里显式写了的设置个数: 15
    DEFAULT_CHARSET(没写,取默认) = utf-8   DEBUG(写了 True) = True
  manage.py diffsettings 输出 13 行,前 6 行:
    ALLOWED_HOSTS = ['*']
    BASE_DIR = PosixPath('<实验目录>')  ###
    DATABASES = {'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': PosixPath('<实验目录>')}}
    DEBUG = True
    INSTALLED_APPS = ['django.contrib.contenttypes']
    MY_UPPER = 2  ###

只有全大写的名字会被读成设置(设置模块里的小写变量、类实例上的属性都不是)。没写的设置来自 django.conf.global_settings。manage.py diffsettings 只列出与默认值不同的项,输出里带 ### 的是默认里不存在的自定义设置。

运行时改设置

实测 · 本机执行输出
  [改写与覆盖]
    导入 proj.extra 时读到的 STALE = True ,fresh() = True
    override_settings(DEBUG=False) 内: settings.DEBUG = False  extra.STALE = True  extra.fresh() = False
    退出 with 之后: settings.DEBUG = True
    直接赋值 settings.DEBUG = False 之后: fresh() = False (进程内一直生效,没有还原)

override_settings 是测试里的正规做法,退出时还原。要记住的陷阱:在模块顶层读取设置(X = settings.DEBUG)只读一次,之后的覆盖影响不到它——需要动态值时,在函数里读,或者用 SimpleLazyObject 之类延迟到使用时。直接赋值 settings.DEBUG = False 在进程内一直生效,不要在生产代码里这么做。

configure() 与类型检查

实测 · 本机执行输出
    第一次 configure OK, DEBUG = True
    第二次 configure → RuntimeError: Settings already configured.
  INSTALLED_APPS 写成字符串: django.core.exceptions.ImproperlyConfigured: The INSTALLED_APPS setting must be a list or a tuple.

不依赖设置文件、在脚本里用 Django 时可以调用 settings.configure(...),但只能调用一次。

分步演示:settings 何时加载

settings 加载
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Python 3.14.7 / Django 6.1.1(默认用 SQLite,其它数据库会在文中注明),用 django.test.Client 或真实的本机服务器进程实测;计时用 time.perf_counter,数字每次运行会有小幅抖动。每个观察都在全新的子进程里做,避免互相污染
    • 没有验证:settings 模块里 import 其它设置模块(from .base import *)的常见分层写法的加载顺序、环境变量 DJANGO_SETTINGS_MODULE 在多进程服务器(gunicorn)里的传递、LazySettings 对线程的影响、settings 被 pickle 的情形
    京ICP备2026067502号-1京公网安备11010502063487号