企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
Django的模板引擎中,功能最强大因此也最复杂的部分,就是模板继承。它允许你构建一个模板基础“骨架”,包含网站中所有的通用元素,然后定义各种**blocks**,以便子模板可以重写。 通过一个例子最容易理解模板继承: ~~~ <!DOCTYPE html> <html lang="en"> <head> <link rel="stylesheet" href="style.css" /> <title>{% block title %}My amazing site{% endblock %}</title> </head> <body> <div id="sidebar"> {% block sidebar %} <ul> <li><a href="/">Home</a></li> <li><a href="/blog/">Blog</a></li> </ul> {% endblock %} </div> <div id="content"> {% block content %}{% endblock %} </div> </body> </html> ~~~ 这个模板文件,我们可以称之为**base.html**, 定义了一个简单的HTML文档骨架,可用于2列的页面布局。子模板的任务就是用内容填充空的块状区域【empty blocks】。 在这个例子中,**block**标记定义了3个区域,可供子模板填充。所有的**block**标记所做的事情,就是告诉模板引擎,子模板可以重写模板的这些部分。 一个子模板看上去大概像这样: ~~~ {% extends "base.html" %} {% block title %}My amazing blog{% endblock %} {% block content %} {% for entry in blog_entries %} <h2>{{ entry.title }}</h2> <p>{{ entry.body }}</p> {% endfor %} {% endblock %} ~~~ 这里的关键是**extends**标记。它告诉模板引擎,这个模板继承于另外一个模板。当模板系统要计算这个模板最终输出时,它首先定位到父模板,在这里,就是**"base.html"**。 此时,模板引擎会注意到**base.html**中的3个**block**标记,然后使用子模板的内容替换这3个区域。基于上面**blog_entries**的值,最终的输出大致像这样: ~~~ <!DOCTYPE html> <html lang="en"> <head> <link rel="stylesheet" href="style.css" /> <title>My amazing blog</title> </head> <body> <div id="sidebar"> <ul> <li><a href="/">Home</a></li> <li><a href="/blog/">Blog</a></li> </ul> </div> <div id="content"> <h2>Entry one</h2> <p>This is my first entry.</p> <h2>Entry two</h2> <p>This is my second entry.</p> </div> </body> </html> ~~~ 要注意由于子模板并没有定义**sidebar**区块,父模板的值被续用。父模板中包裹在**{% block %}**标记的内容,总是作为最后的备选。 你可以使用任意多的继承层级。一种常用的模板继承方式是如下的3级步骤: * 创建一个**base.html**模板文件,囊括了网站的主体外观。 * 针对网站中每一个模块,创建一个**base_SECTIONNAME.html**模板文件。比如,**base_news.html**,**base_sports.html**。这些模板全都继承自**base.html**,并且包含模块特定的样式和设计。 * 为每种类型的页面创建单独的模板,比如一则新闻,或者一篇博客。这些模板继承自对应的功能模块模板。 这种方式可以最大化代码复用,并且很容易对共享内容区域添加新元素,比如网站模块导航。 以下是处理继承时的一些注意事项: * 如果你在一个模板中使用**{% extends %}**,那它必须是模板中的第一个标记。否则模板继承将失效。 * 在基础模板中**{% block %}**标记越多越好。记住,子模板不需要定义所有的父区块,所以你可以对一些区块使用合理的默认值填充,然后只需要定义那些你确实需要的区块。钩子多总比钩子少好。 * 如果你发现自己正在一组模板中做重复的事情,那可能就意味着,你需要在父模板中,将这部分内容移至**{% block %}**中。 * 如果你需要从父模板中获取区块内容,**{{ block.super }}**变量可以完成这件事情。当你不想完全覆盖,而只想在父区块中增加内容时,这个会非常有用。使用**{{ block.super }}**插入的数据不会被自动转义(详见[HTML自动转义](https://docs.djangoproject.com/en/1.10/ref/templates/language/#automatic-html-escaping)),因为如果有必要,它在父模板中就已经被转义过了。 * 为了额外的可读性,你可以可选的在**{% endblock %}**标记中给定一个名字。比如: ~~~ {% block content %} ... {% endblock content %} ~~~ 在较大的模板文件中,这个技巧可以帮助你识别到底是哪个**{% block %}**标记结束。 最后,记住你不能在同样的模板文件中,定义同名的多个**block**标记。之所以有这个限制,是因为一个**block**标记是双向的影响。就是说,一个block标记的作用,不仅仅是[向子级]提供可填充区域 -- 它还在父端定义了填充的内容。如果在一个模板中有2个同名的**block**标记,那么父模板就不知道该使用哪一个区块的内容。