Open edX Platform Django 配置体系解析:从 openedx/envs/common.py 到 LMS/CMS 环境设置的三层架构
2026/9/16 15:11:04 网站建设 项目流程

Open edX Platform Django 配置体系解析:从 openedx/envs/common.py 到 LMS/CMS 环境设置的三层架构

【免费下载链接】openedx-platformThe Open edX LMS & Studio, powering education sites around the world!项目地址: https://gitcode.com/GitHub_Trending/ed/openedx-platform

Open edX 平台(edx-platform)通过三层 Django 配置模块组织全站配置:平台级公共配置 openedx/envs/common.py(约 3062 行)、LMS 配置 lms/envs/common.py(约 3306 行)、CMS/Studio 配置 cms/envs/common.py(约 1359 行)。本文以官方设置参考文档 docs/references/settings.rst 为骨架,讲清这套配置的分层继承关系、Derived派生设置的计算机制、FEATURES特性开关代理,以及运维人员如何安全地覆盖设置项——读完后你可以独立完成"查看最终生效配置、编写自定义 DJANGO_SETTINGS_MODULE、排查配置覆盖链"三类操作。

一、设置参考文档是如何自动生成的

官方设置参考页 settings.rst 本身只有 25 行,其内容不是手写的,而是通过 Sphinx 指令自动从源码抽取:

Settings ======== This is the list of (non-toggle) Django settings defined in the ``common.py`` modules of edx-platform. .. note:: Toggle settings, which enable or disable a specific feature, are documented in the :ref:`feature toggles <featuretoggles>` section. Platform-Wide Settings ---------------------- .. settings:: :folder_path: openedx/envs/common.py LMS Settings ------------ .. settings:: :folder_path: lms/envs/common.py CMS Settings ------------ .. settings:: :folder_path: cms/envs/common.py

三个关键机制值得注意:

  1. .. settings::指令:由 Sphinx 扩展code_annotations.contrib.sphinx.extensions.settings提供,在 docs/conf.py 中注册。构建文档时,它扫描指定文件里所有大写顶层变量(即 Django 设置名),并结合源码中的注释生成条目化的设置列表,同时把每个设置链接到对应源码行。
  2. 设置与功能开关分而治之:文档明确说明 toggle 类设置(形如# .. toggle_name: settings.XXX注释块)不在此页,而是收录在 featuretoggles.rst 中。以 lms/envs/common.py 为例,其中包含 69 处toggle_name注释(如settings.ENABLE_MASQUERADEsettings.DISPLAY_HISTOGRAMS_TO_STAFF),CMS 侧有 16 处。这种"非开关设置看 settings 页、开关设置看 toggles 页"的划分是查阅配置时的第一条经验法则。
  3. 文档构建即代码检查:docs/conf.py 在构建前设置DJANGO_SETTINGS_MODULE = 'docs.docs_settings'并执行django.setup(),确保 LMS 与 Studio 全部代码可被成功导入。因此设置文件里任何导入错误都会直接暴露为文档构建失败。

二、三层设置的分层继承关系

整个配置体系是一棵"星号导入 + 逐项覆盖"的树:

openedx/envs/common.py 平台级:LMS 与 CMS 共享的基础默认值(路径、Django 内建项、缓存、DB、模板、Celery、JWT……) ├── lms/envs/common.py LMS:`from openedx.envs.common import *` 后叠加 LMS 专属设置与 FEATURES │ ├── lms/envs/test.py 单元测试环境,末尾调用 derive_settings(__name__) │ └── lms/envs/production.py 生产环境,加载 LMS_CFG 指向的 YAML 覆盖 └── cms/envs/common.py CMS/Studio:同样 `from openedx.envs.common import *` 后叠加 CMS 专属设置 ├── cms/envs/test.py └── cms/envs/production.py

继承方式的证据直接写在源码里:lms/envs/common.py 与 cms/envs/common.py 都执行from openedx.envs.common import *(附带pylint: disable=wildcard-import),随后各自定义FEATURES = FeaturesProxy(globals())特性开关代理(lms/envs/common.py#L72、cms/envs/common.py#L58)。

openedx/envs/common.py 的模块 docstring 还特别警告了一个实操陷阱:

WARNING: Mutable values defined in this file may be unintentionally modified downstream…… if an LMS settings module modifies a mutable value defined here, the final value of the corresponding CMS setting may also be affected. To avoid this risk, create a deep copy of the value in the module that modifies it.

即:因为 CMS 会导入 LMS 会导入的共享可变值,若你的下游设置文件直接就地修改(mutate)某个列表/字典,可能污染其他服务的最终配置——正确做法是深拷贝后再修改。

按 ADR 0022-settings-simplification.rst 的目标结构,这套分层的定位是:

  • openedx/envs/common.py:尽可能集中 LMS/CMS 共享配置,给出合理的、生产可用的默认值(production-ready defaults);对密钥类设置给出明显错误的占位默认值(obviously-wrong defaults),确保不会被误用于生产;
  • lms/envs/common.py/cms/envs/common.py:在此之上扩展出各服务的生产可用配置,并作为运行管理命令的默认设置文件(可用DJANGO_SETTINGS_MODULE覆盖);
  • 该 ADR 还明确了当前配置体系的演进方向:逐步把production.py中的默认值"上浮"到common.py,让运维通过自定义DJANGO_SETTINGS_MODULE派生自common.py来完成覆盖。

三、平台级核心设置逐项解读(openedx/envs/common.py)

以下按 openedx/envs/common.py 源码中的分区(##### Section Name #####头注释)梳理最重要的设置项。完整列表请在文档构建产物中通过.. settings::指令生成的页面查阅,此处给出高频项与源码依据。

3.1 路径与运行基础(Paths / Django Built-Ins)

设置默认值说明源码位置
REPO_ROOT仓库根目录path(__file__).abspath()推导,其余路径常量都基于它openedx/envs/common.py#L98-L104
COURSES_ROOT/DATA_DIRENV_ROOT / "data"课程数据根目录,ENV_ROOT为存放 edx-platform 的上级目录(venv 同级)同上
DEBUGFalse生产默认关闭调试L108
USE_TZ/TIME_ZONETrue/'UTC'统一 UTC 时区L110-L111
SECRET_KEY'dev key'典型"明显错误"占位值,生产环境必须覆盖L118
SECURE_PROXY_SSL_HEADER('HTTP_X_FORWARDED_PROTO', 'https')源码注释强调:启用后服务器必须位于会剥离该头的代理之后,否则用户可伪造 https 状态L120-L123
SESSION_ENGINEdjango.contrib.sessions.backends.cache会话存缓存;序列化器为openedx.core.lib.session_serializers.PickleSerializerL129-L131
CSRF_COOKIE_AGE60*60*24*7*52(一年)源码同时警告CSRF_COOKIE_SECURE = False的默认值"强烈建议在面向终端用户的环境中覆盖"L133-L136
ALLOWED_HOSTS['*']仅适合开发/测试L138
ROOT_URLCONFDerived(lambda settings: f'{settings.SERVICE_VARIANT}.urls')派生设置的典型用法:LMS 得到lms.urls、CMS 得到cms.urlsL143
X_FRAME_OPTIONS'DENY'防点击劫持,设为'ALLOW'可关闭L141

3.2 缓存与数据库

CACHES 定义了 7 个缓存别名:defaultgeneralconfigurationstaticfilescourse_structure_cache(超时 1 周)、celery(超时 7200 秒)、mongo_metadata_inheritance(超时 300 秒),全部默认使用PyMemcacheCache指向localhost:11211,并统一使用common.djangoapps.util.memcache.safe_key作为 KEY_FUNCTION、ignore_exc: True(缓存故障不阻断业务)。

DATABASES 默认声明三个 MySQL 连接:default(库名edxapp,开启ATOMIC_REQUESTS)、read_replica(读副本)、student_module_history(库名edxapp_csmh,存放学习进度历史)。配套的DATABASE_ROUTERS使用 StudentModuleHistoryExtendedRouter 把StudentModuleHistory相关表路由到独立库。注释中说明edxapp-migrate脚本保证除read_replica外的库会同时为 LMS 和 CMS 执行迁移。

3.3 模板系统:Django 与 Mako 双引擎

TEMPLATES 同时注册了两个后端:

  • django后端APP_DIRS: False,模板目录为PROJECT_ROOT/templatescommon/templates等;loader 链中特意加入 Mako 感知的 loader(ThemeTemplateLoaderMakoFilesystemLoaderMakoAppDirectoriesLoader),以便在 Django 模板中 include Mako 模板(如main_django.html)。
  • mako后端BACKENDcommon.djangoapps.edxmako.backend.Mako,其DIRS是一个Derived(make_mako_template_dirs)——该函数 会在启用综合主题(ENABLE_COMPREHENSIVE_THEMING)时,把各主题的模板目录插入MAKO_TEMPLATE_DIRS_BASE头部。

相关配套设置:MAKO_MODULE_DIR(Mako 编译产物目录,L504 也是Derived,按SERVICE_VARIANT区分 LMS/CMS 的临时目录)、CONTEXT_PROCESSORS(L520-L530,包含帮助系统的help_tokens.context_processor与站点配置的configuration_context)。

3.4 其他主要分区

openedx/envs/common.py 后半部分按主题划分的分区(源码中的分区头注释)包括:Optional Apps(可选项INSTALLED_APPS扩展)、Django Rest Framework、Celery、RedirectMiddleware、Django Debug Toolbar、JWT、Features、CAPA External Code Evaluation、CSRF、Cross-domain Requests、Social Media、Google Analytics、Block Structures、Bulk Email、Video(含图片/字幕存储与视频管线)、Parental Controls、Instructor Downloads、Registration、Course Enrollment Modes、Enterprise Api Client、ModuleStore、Micro-frontends、Swift、SAML、django-fernet-fields、django-simple-history、Django OAuth Toolkit、Profile Image、XBlock、Built-in Blocks Extraction 等。这些分区的设置都会出现在文档构建产物中;开发调试时可直接在源码中按分区名定位。

四、核心机制一:Derived派生设置

Derived是理解整套配置体系的钥匙。定义在 openedx/core/lib/derived.py:

class Derived(t.Generic[T]): """ A temporary Django setting value, defined with a function which generates the setting's eventual value. Said function (`calculate_value`) should accept a Django settings module, and return a calculated value. To ensure that application code does not encounter an instance of this class in your settings, be sure to call `derive_settings` somewhere in your terminal settings file. """ def __init__(self, calculate_value: t.Callable[[Settings], T]): self.calculate_value = calculate_value

工作方式是"延迟求值":

  1. 设置先被赋值为一个Derived(calculate_value)占位对象,而不是具体值;
  2. 所有设置模块(shared → service → 下游覆盖)全部加载完成后,终端设置文件调用derive_settings(module_name)
  3. derive_settings 遍历该模块所有大写字母开头的顶层变量(正则^[A-Z][A-Z0-9_]*$),递归评估其中嵌套在 dict/list/tuple/set 里的Derived对象(见 _derive_recursively),用calculate_value(settings)的返回值就地替换。

这样设计解决的问题是:某个设置的合理默认值依赖另一个设置的最终值,而后者可能被你下游覆盖。典型实例:

  • ROOT_URLCONF = Derived(lambda settings: f'{settings.SERVICE_VARIANT}.urls')(openedx/envs/common.py#L143):只有等SERVICE_VARIANT被最终确定后才计算;
  • LOCALE_PATHS = Derived(_make_locale_paths)(L222):依赖PREPEND_LOCALE_PATHS与主题设置;
  • LOCALE_PATHS的推导函数 _make_locale_paths 会先取PREPEND_LOCALE_PATHS,再追加conf/locale,启用主题时继续追加主题 locale 路径。

两条硬约束(源码 docstring 明确):

  • 你的终端设置文件必须调用derive_settings(__name__),否则应用代码会拿到Derived实例而非真实值。仓库内现有调用点见 lms/envs/test.py#L339 与 cms/envs/test.py#L193;
  • Derived值可以被直接赋成一个普通值来覆盖——此时它不再参与推导,等价于"手工指定最终值"。

五、核心机制二:FEATURES特性开关代理

LMS 与 CMS 的common.py都不再用裸字典管理开关,而是FEATURES = FeaturesProxy(globals())(lms/envs/common.py#L71-L72)。FeaturesProxy是 openedx/core/lib/features_setting_proxy.py 中实现的MutableMapping:它以所在设置模块的globals()为数据源,把settings.FEATURES['ENABLE_MASQUERADE']这样的字典访问映射回模块级变量ENABLE_MASQUERADE的读写。

这带来两个实际后果:

  • 覆盖开关的标准写法仍是"直接重赋模块级变量"(如ENABLE_MASQUERADE = False),settings.FEATURES[...]只是读取语法糖;
  • 由于代理绑定在globals()上,LMS 与 CMS 各自维护独立的FEATURES命名空间,修改互不干扰。

# .. toggle_name:注释块的开关(如 lms/envs/common.py#L129-L138 的ENABLE_DJANGO_ADMIN_SITE)会被文档管线收集到 featuretoggles.rst 页面,注释块中的toggle_defaulttoggle_descriptiontoggle_warning字段即页面展示内容。

六、LMS 与 CMS 服务级设置要点

6.1 lms/envs/common.py

除继承自平台级的全部设置外,LMS 层还定义了:PLATFORM_NAMECOURSE_IDT_REGEX、课程/使用键的正则常量、COURSES_API_*PAYMENT_PROCESSOR_*、邮件/SMTP、ENTERPRISE_*角色常量导入(lms/envs/common.py#L48-L62)、CACHES扩展(如bulk_email别名)等。文件头部 lms/envs/common.py#L14-L32 还写明了一条重要惯例——扩展列表类设置优先使用EXTRA后缀的新变量(如CELERY_EXTRA_IMPORTSXBLOCK_EXTRA_MIXINS),而不是就地替换整个列表,以便平台后续合并时不丢失你的条目。

6.2 cms/envs/common.py

CMS 层聚焦 Studio 侧能力,代表性设置(cms/envs/common.py#L62-L160):

设置默认说明
STUDIO_NAME/STUDIO_SHORT_NAME"Your Platform Studio"/"Studio"品牌名
SECRET_KEY'dev key'再次覆盖为占位值,生产必须替换
ENABLE_CREATOR_GROUPTrue开启后仅课程创建者组可建课
ENABLE_CONTENT_LIBRARIESTrue内容库(仅 split mongo 课程支持)
ALLOW_COURSE_RERUNSTrue控制 Studio 首页 Re-run Course 入口
ENABLE_SEPARATE_ARCHIVED_COURSESTrue归档课程单独列表展示
ENABLE_GRADE_DOWNLOADSTrue支持成绩下载
GITHUB_PUSH/STUDIO_REQUEST_EMAILFalse/''Git 推送、开课申请邮箱
ENABLE_MAX_FAILED_LOGIN_ATTEMPTSFalse失败登录锁定

七、实操指南:查看最终配置与自定义覆盖

7.1 查看某设置模块的完整生效值

ADR 计划引入的dump_settings管理命令已落地:openedx/core/djangoapps/util/management/commands/dump_settings.py,对应测试在 test_dump_settings.py。用法示例(在 venv 中,以 LMS 测试设置为前提):

DJANGO_SETTINGS_MODULE=lms.envs.test ./manage.py lms dump_settings

它会以 JSON 输出当前DJANGO_SETTINGS_MODULE加载后的全部顶层设置,适合在调整覆盖链后验证"最终值到底是什么"。

7.2 编写自定义生产设置模块

按 ADR 的目标结构,自定义设置模块(例如部署工具生成的lms_prod.py)应遵循三步:

# <your>/lms_prod.py(示例骨架) from lms.envs.common import * # 1. 继承服务级公共默认 # 2. 用生产值替换所有"明显错误"占位(SECRET_KEY、ALLOWED_HOSTS、 # CSRF_COOKIE_SECURE、SECURE_PROXY_SSL_HEADER 前提条件……) from openedx.core.lib.derived import derive_settings derive_settings(__name__) # 3. 渲染所有 Derived 值

要点回顾:

  • 必须覆盖"明显错误"默认值,否则会带着SECRET_KEY='dev key'这类占位值上线;
  • 必须调用derive_settings(__name__),否则Derived占位不会计算;
  • 修改共享可变值前先深拷贝(见 openedx/envs/common.py 的 WARNING);
  • 对列表类设置优先使用XXX_EXTRA_YYY变量扩展而非整表替换。

7.3 测试环境配置

单测使用的 openedx/envs/test.py 集中了 LMS/CMS 共享的测试值(如把ENABLE_DISCUSSION_SERVICE置为False加速测试),服务级测试模块 lms/envs/test.py 与 cms/envs/test.py 分别在其末尾执行derive_settings(__name__)完成派生设置渲染——这也是"终端设置文件负责调用derive_settings"这一约束在仓库内的直接范例。

八、延伸阅读与限制说明

  • 配置简化工程的背景、动机与完整行动路线(含对 Tutor 间接层问题的剖析)见 ADR:docs/decisions/0022-settings-simplification.rst;
  • 功能开关完整列表见 docs/references/featuretoggles.rst;
  • 各环境模块的职责分工:common.py(默认值)→production.py(加载LMS_CFG/CMS_CFG指向的 YAML 覆盖)→test.py(单测)。当前仓库中 YAML 覆盖路径仍在演进,具体取舍以 ADR 所述两条路线(保留简化版 YAML schema,或完全转向自定义DJANGO_SETTINGS_MODULE)为准;
  • 本文所有行号与取值均基于当前仓库快照(openedx/envs/common.py3062 行、lms/envs/common.py3306 行、cms/envs/common.py1359 行),升级平台后请以对应版本的源码为准。

【免费下载链接】openedx-platformThe Open edX LMS & Studio, powering education sites around the world!项目地址: https://gitcode.com/GitHub_Trending/ed/openedx-platform

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询