Django:覆盖和扩展应用程序模板

Posted

技术标签:

【中文标题】Django:覆盖和扩展应用程序模板【英文标题】:Django: Overriding AND extending an app template 【发布时间】:2011-04-27 10:29:53 【问题描述】:

如果您想覆盖 django 中应用程序附带的模板(在 app/templates/app/ 中),您可以在另一个目录中创建一个同名模板,模板加载器会在应用程序的模板目录之前检查该模板。如果您只想覆盖模板的某些块,您还必须复制整个模板并更改该块,这实际上不是很干。

有没有人知道一种方法来覆盖原始模板,同时扩展它,以便您只需覆盖您想要更改的特定块? (这样做没有更改模板的名称,因为在某些情况下您可能必须更改视图以使其与另一个模板一起使用)

编辑:正如 Adam Taylor 在来自 Django 1.9 on 的 cmets 中指出的那样,这在没有任何黑客攻击的情况下是可能的。

【问题讨论】:

@paulo:据我所知,当然有关于覆盖应用程序模板的文档,因为这是日常实践,但我从未见过任何关于扩展同名模板的内容。如果您知道它在文档中,请指向那个地方... docs.djangoproject.com/en/dev/ref/contrib/admin/… 那里的方法仅适用于特定于应用程序的管理模板,因为这些模板与原始模板不同。我猜这就是为什么这是在 admin 文档中而不是在一般文档中的原因;) 致所有使用 Django 1.9+ 的人:现在已内置于 Django。来自the "Templates" section of the Django 1.9 release notes 的引用:“Django 模板加载器现在可以递归地扩展模板。” @AdamTaylor 谢谢,这对隐藏在发行说明中的​​有趣功能很有帮助。 【参考方案1】:

我认为this related question 的回答很中肯;目前,最好的解决方案似乎是使用来自django-apptemplates package on PyPI 的自定义模板加载器,因此您可以使用pip 来安装它(例如pip install django-apptemplates)。

模板加载器允许您在特定应用程序中扩展模板;例如,要扩展管理界面的索引页面,您可以添加

'apptemplates.Loader',

settings.py 中的TEMPLATE_LOADERS 列表,然后使用

% extends "admin:admin/index.html" %

在您的模板中。

【讨论】:

我正在进行的一个项目使用这个 sn-p 来解决这个确切的问题。我很惊讶更多的人没有点击它:一个想要调整现有页面部分的应用程序。 我认为这是一个常见问题,但人们通过复制完整模板来弥补。如果有人尝试过,覆盖和扩展通常以无限递归结束。此票证中描述了该问题:code.djangoproject.com/ticket/15053【参考方案2】:

Django 1.9 及更高版本有this feature:

Django 模板加载器现在可以递归扩展模板。

所以你可以从app1 获得一个文件,其内容如下:

app1/templates/example/foobar.html:

<p>I'm from app1</p>
% block main %% endblock %

您可以使用app2 中的文件对其进行扩展(注意模板名称example/foobar.html 相同):

app2/templates/example/foobar.html:

% extends "example/foobar.html" %
% block main %
  <p>I'm from app2</p>
% endblock %

最终结果应该是:

<p>I'm from app1</p>
<p>I'm from app2</p>

【讨论】:

我已经尝试过这个设置,但它似乎不起作用。 @HoHo 你用的是哪个 Django 版本? 我使用的是 1.10 -- 如果app1 有一组相互关联的模板(比如app1/templates/app1/base.html,由app1/templates/app1/subfolder/subsite.html 扩展),模板app2 应该如何扩展这两者模板?我想用你的方法——在app1中复制文件夹层次结构和文件名——会起作用,但它似乎不起作用。【参考方案3】:

作为一个更新,因为这似乎是一个流行的问题。我使用overextends app 没有任何问题。它提供了一个新的关键字overextends,允许扩展同名模板。

而且很容易用 pip 安装:

 pip install -U django-overextends

【讨论】:

仅供参考 Django 1.4 的上一个版本是 0.3.2,后者需要 1.7【参考方案4】:

在 Django wiki 中,Simon Willison 提出了a trick 来实现“自扩展模板”的效果。但是,如果您使用的是 app_directories 模板加载器,则它并不直接适用。

在新目录中符号链接应用程序的模板目录可能会有所帮助。

【讨论】:

谢谢,这也不是我想要的 100%,但我认为你不能再接近了;在为不同网站定制模板时已经在做类似的事情了!【参考方案5】:

[更新]

我误读了这个问题,我的原始答案仅适用于具有内置模板扩展机制的管理应用程序。对于缺乏这种机制的其他应用程序,我只会分叉原始模板,而不是像所选答案建议的那样摆弄自定义模板加载器。如果你担心分叉,你也可以实现一个扩展机制,如果你认为值得的话,可以回馈原始项目。


[原答案]

直接从罚款manual: 由于管理模板的模块化设计,通常不需要也不建议替换整个模板。最好只覆盖您需要更改的模板部分。

要继续上面的示例,我们希望在页面模型的历史工具旁边添加一个新链接。查看 change_form.html 后,我们确定我们只需要覆盖 object-tools 块。因此这里是我们新的 change_form.html :

% extends "admin/change_form.html" %
% load i18n %
% block object-tools %
% if change %% if not is_popup %
  <ul class="object-tools">
    <li><a href="history/" class="historylink">% trans "History" %</a></li>
    <li><a href="mylink/" class="historylink">My Link</a></li>
    % if has_absolute_url %
        <li><a href="../../../r/ content_type_id / object_id /" class="viewsitelink">
            % trans "View on site" %</a>
        </li>
    % endif%
  </ul>
% endif %% endif %
% endblock %

就是这样!如果我们将此文件放在 templates/admin/my_app 目录中,我们的链接将出现在每个模型的更改表单上。

【讨论】:

感谢您发布此信息,但我的问题与 ADMIN 模板无关。我正在寻找使用(前端)模板做这样的事情,例如。附带第三方应用程序,我可能只是想更改一个块,然后复制整个其余部分以覆盖一部分,这对我来说看起来像是违反 DRY... @lazerscience:这是相同的过程,除了在您的模板目录中,您应该遵循与您要覆盖的模板相同的目录结构。检查:docs.djangoproject.com/en/dev/ref/templates/api/… 我认为这仍然不能回答原始问题。假设niceapp.views.index() 使用模板niceapp/index.html。如果我想使用原始视图但自定义模板,我可以在我的项目或我自己的应用程序的templates 目录中创建一个niceapp/index.html。但是如何扩展原始并覆盖一些块,而不是复制整个模板?说% extend "niceapp/index.html" % 显然不能胜任。 @akaihola:你是对的,我误读了这个问题。已更新。【参考方案6】:

一个简单的方法是这样的:

假设您要扩展和覆盖 django/contrib/admin/templates/admin/change_form.html。

首先,将原始 change_form.html 复制到应用的模板目录,并将其重命名为 myapp/templates/admin/original_change_form.html。 (您也可以将其作为符号链接。)

其次,在 myapp/templates/admin 中创建自己的 change_form.html。在它的顶部,放置以下内容:

% extends "admin/original_change_form.html" %

简单!

【讨论】:

这实际上不是一个糟糕的解决方案,尤其是如果您的系统支持符号链接。

以上是关于Django:覆盖和扩展应用程序模板的主要内容,如果未能解决你的问题,请参考以下文章

Django 模板:通过扩展模板覆盖包含的子模板块

在 django admin 中覆盖模板

Django:覆盖每个应用程序而不是每个项目的“不可覆盖”管理模板?

Django覆盖模板静态文件位置

如何覆盖 django 模板渲染方法处理

无法覆盖 django-allauth 模板