Django 如何覆盖第三方应用或 django.contrib.admin 的内置模板
【免费下载链接】djangoThe Web framework for perfectionists with deadlines.项目地址: https://gitcode.com/GitHub_Trending/dj/django
当你在项目中使用了第三方应用或django.contrib.admin这类 contrib 应用,却想改动它们自带的页面外观(比如给 admin 后台加一个 logo、改掉 admin 顶部标题)时,Django 的模板加载机制允许你用自己写的模板文件替换它们内置的模板,而不用修改这些应用的源码。本文介绍两条官方支持的路径:把覆盖模板放在项目级templates目录,或放在某个应用的templates目录,以及如何用{% extends %}只改模板中的某一块内容。适用前提是项目使用默认项目模板(django-admin startproject生成),settings.py中已有TEMPLATES设置。
先确认 TEMPLATES 设置
覆盖模板依赖两个配置项:
DIRS:模板引擎在文件系统上查找模板的目录列表(搜索路径),默认为空列表。APP_DIRS:是否在已安装应用的内部查找模板,默认为False;startproject生成的默认settings.py会将其设为True。
这两项的完整说明见 settings 文档,模板加载的底层机制(DIRS选项、loader 类型)见 模板 API 文档。
主路径:在项目级 templates 目录中覆盖
这是官方文档推荐的常用方式,以 How to override templates 的blog第三方应用为例。
假设第三方应用blog提供了blog/post.html和blog/list.html两个模板。首先确保settings.py中TEMPLATES配置了DIRS,指向项目根目录下的templates目录:
from pathlib import Path BASE_DIR = Path(__file__).resolve().parent.parent INSTALLED_APPS = [ ..., "blog", ..., ] TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", "DIRS": [BASE_DIR / "templates"], "APP_DIRS": True, # ... }, ]如果项目是用默认项目模板创建的,TEMPLATES和BASE_DIR已经存在,需要修改的只是DIRS这一项。
然后在项目根目录的templates目录里,按被覆盖模板的同名路径新建文件:
templates/ blog/ list.html post.html工作原理:模板加载器先查找DIRS中的目录。当blog应用的视图请求blog/post.html和blog/list.html时,加载器会返回你刚创建的文件,而不是blog应用自带的版本。这也是文档给出的行为判断依据:请求模板时拿到的是你创建的覆盖文件。
可选路径:在某个应用的 templates 目录中覆盖
由于覆盖的是项目内应用之外的模板,更常见的做法是上面的项目级目录方式;但文档同样支持把覆盖模板放进某个应用的模板目录。前提是确认APP_DIRS为True:
TEMPLATES = [ { # ... "APP_DIRS": True, # ... }, ]如果想把blog/list.html和blog/post.html的覆盖版本放进名为myapp的应用,目录结构为:
myapp/ templates/ blog/ list.html post.htmlAPP_DIRS设为True后,模板加载器会到各应用的templates目录里查找并找到这些模板。
需要注意优先级:如果应用目录和项目目录都含有同名覆盖模板,默认 Django 模板加载器会优先从项目级目录加载,即DIRS先于APP_DIRS被搜索。
覆盖 admin 模板并保留原内容
对于django.contrib.admin的模板,可以直接复制改写,也可以用{% extends %}在覆盖的同时继承原模板,只替换其中某一段。官方示例 是在 admin 首页添加自定义 logo:
{% extends "admin/base_site.html" %} {% block branding %} <img src="link/to/logo.png" alt="logo"> {{ block.super }} {% endblock %}上面文件保存在templates/admin/base_site.html(示例中link/to/logo.png是文档示意值,请替换为你自己的 logo 路径)。文档对这段示例的要点说明:
- 该文件位于项目级
templates目录,用来覆盖admin/base_site.html; - 新模板
extends的正是被覆盖的同一个模板admin/base_site.html; - 只替换
branding这一个 block,用block.super保留原有内容; - 模板其余部分原样继承自
admin/base_site.html。
之所以可行,是因为模板加载器在解析extends标签时,不会把已经加载的那个覆盖模板(即templates/admin/base_site.html)算进去。仓库中真实的 admin 模板可以对照查看:base_site.html 和 base.html 都定义了brandingblock。
不继承、整体复制改写的做法
Django 教程第 7 部分 展示了另一种做法:直接复制admin/base_site.html再编辑。步骤是:
- 在项目目录(如
djangotutorial)下创建templates目录; - 在
settings.py的TEMPLATES中加"DIRS": [BASE_DIR / "templates"]; - 在
templates下建admin目录,把 Django 源码中的admin/base_site.html(默认 admin 模板目录位于django/contrib/admin/templates)复制进去; - 编辑文件,例如把
{{ site_header|default:_('Django administration') }}(连同花括号)替换为站点名称,得到一个定制的{% block branding %}。
如果找不到 Django 源码在哪,文档给出的定位命令是:
$ python -c "import django; print(django.__path__)"教程还指出:Django 任何默认 admin 模板都可以用同样方式覆盖——从默认目录复制到你的自定义目录,然后修改。另有一个边界说明:仅仅改 admin 站点标题这类定制,实际项目中更简单的方式是使用AdminSite.site_header属性,模板覆盖更适合需要改动页面结构的情况。
结果验证与注意事项
- 验证方式:文档对覆盖生效的判断标准是加载器行为本身——当应用视图请求被覆盖的模板名时,加载器返回你在
DIRS或应用templates目录中创建的文件。对 admin 而言,访问后台页面即可看到branding区域的定制内容(新 logo 或新标题)取代了默认内容。 - 搜索顺序:
DIRS先于APP_DIRS,项目级目录中的同名模板会先于应用目录中的模板被找到,两处同时放同名文件时以此为准。 - 内置表单控件模板:如果你的目标是覆盖 widget 等表单内置模板(而不是普通页面模板),必须使用
TemplatesSettingrenderer,做法与本文的覆盖流程相同,详见 表单 renderer 文档中的 “Overriding built-in widget templates”。 - 加载器适用范围:上述“默认 Django 模板加载器”的行为描述针对内置 Django 模板引擎;若项目改用 Jinja2 后端或自定义 loader,模板查找行为以对应后端为准。
参考资料:How to override templates、教程 7:模板定制章节、settings 文档。
【免费下载链接】djangoThe Web framework for perfectionists with deadlines.项目地址: https://gitcode.com/GitHub_Trending/dj/django
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考