作为一个Web框架,Django需要一种动态生成HTML的便捷方式。最常见的方法依赖于模板。模板包含所需HTML输出的静态部分以及描述如何插入动态内容的一些特殊语法。
Django项目可以配置一个或多个模板引擎(如果不使用模板,甚至可以为零)。Django为其自己的模板系统提供内置后端,创造性地称为Django模板语言(DTL),以及流行的替代Jinja2。可以从第三方获得其他模板语言的后端。
Django定义了一个标准API,用于加载和呈现模板,无论后端如何。加载包括查找给定标识符的模板并对其进行预处理,通常将其编译为内存中表示。渲染意味着使用上下文数据插入模板并返回结果字符串。
由于历史原因,模板引擎的通用支持和Django模板语言的实现都存在于django.template 命名空间中。
你可能已经注意到我们在例子视图中返回文本的方式有点特别。 也就是说,HTML被直接硬编码在 Python代码之中。
------------------------------------urls.py-----------------------------------------------
path('current_datetime', views.current_datetime),
------------------------------------views.py---------------------------------------------
def current_datetime(request):
now = datetime.datetime.now()
html = "It is now %s." % now
return HttpResponse(html)
尽管这种技术便于解释视图是如何工作的,但直接将HTML硬编码到你的视图里却并不是一个好主意。 让我们来看一下为什么:
对页面设计进行的任何改变都必须对 Python 代码进行相应的修改。 站点设计的修改往往比底层 Python 代码的修改要频繁得多,因此如果可以在不进行 Python 代码修改的情况下变更设计,那将会方便得多。
Python 代码编写和 HTML 设计是两项不同的工作,大多数专业的网站开发环境都将他们分配给不同的人员(甚至不同部门)来完成。 设计者和HTML/CSS的编码人员不应该被要求去编辑Python的代码来完成他们的工作。
程序员编写 Python代码和设计人员制作模板两项工作同时进行的效率是最高的,远胜于让一个人等待另一个人完成对某个既包含 Python又包含 HTML 的文件的编辑工作。
基于这些原因,将页面的设计和Python的代码分离开会更干净简洁更容易维护。 我们可以使用 Django的 模板系统 (Template System)来实现这种模式,这就是本章要具体讨论的问题。
Django模板只是一个文本文档或使用Django模板语言标记的Python字符串。一些构造由模板引擎识别和解释。主要是变量和标签。
使用上下文呈现模板。渲染将变量替换为其值,这些值在上下文中查找,并执行标记。其他所有内容都按原样输出。
Django模板语言的语法涉及四种结构。
组成:HTML代码+逻辑控制代码
模板只是一个文本文件。它可以生成任何基于文本的格式(HTML,XML,CSV等)。
模板包含变量,这些变量在评估模板时将替换为值,而变量则包含控制模板逻辑的标记。
为什么使用基于文本的模板而不是基于XML的模板(如Zope的TAL)?我们希望Django的模板语言不仅可用于XML / HTML模板。在World Online,我们将其用于电子邮件,JavaScript和CSV。您可以将模板语言用于任何基于文本的格式。
变量从上下文输出一个值,这是一个类似于dict的对象,它将键映射到值。
变量(使用双大括号来引用变量):语法格式: {{var_name}}
当模板引擎遇到变量时,它会计算该变量并将其替换为结果。变量名由字母数字字符和下划线()的任意组合组成,但不能以下划线开头。重要的是,变量名称中不能包含空格或标点符号
在 Django 模板中遍历复杂数据结构的关键是句点字符 (.)
注意点
从技术上讲,当模板系统遇到一个点时,它会按以下顺序尝试以下查找:
1.字典查找
2.属性或方法查找
3.数字索引查找
如果结果值是可调用的,则调用它时不带参数。调用的结果成为模板值。
此查找顺序可能会导致覆盖字典查找的对象出现一些意外行为。
字典查找,属性查找和列表索引查找使用点表示法实现:
{{ my_dict.key }}
{{ my_object.attribute }}
{{ my_list.0 }}
# 首先,句点可用于访问列表索引,例如:
-------------------------------views.py--------------------------------
def test(request):
test_list = ["xiaoming","baidu"]
return render(request,"test.html",locals())
--------------------------------test.html------------------------------
hello {{ test_list }}
hello {{ test_list.0 }}
hello {{ test_list.1 }}
#假设你要向模板传递一个 Python 字典。 要通过字典键访问该字典的值,可使用一个句点:
-------------------------------views.py--------------------------------
def test(request):
test_dic = {"name":"jie","age":"18"}
return render(request,"test.html",locals())
--------------------------------test.html------------------------------
hello {{ test_dic }}
hello {{ test_dic.name }}
hello {{ test_dic.age }}
#这个例子使用了一个自定义的类,。你同样可以在模板中使用句点来访问这些属性:
-------------------------------views.py--------------------------------
class test_class:
def __init__(self,name,age):
self.name = name
self.age = age
def test(request):
c = test_class("xiaobai","16")
return render(request,"test.html",locals())
--------------------------------test.html------------------------------
hello {{ c }}
hello {{ c.name }}
hello {{ c.age }}
#同样,也可以通过句点来访问对象的属性。 比方说, Python 的 datetime.date 对象有
#year 、 month 和 day 几个属性,你同样可以在模板中使用句点来访问这些属性:
-------------------------------views.py--------------------------------
from django.template import Template, Context
import datetime
def test(request):
d = datetime.date(2019,8,23)
return render(request,"test.html",locals())
--------------------------------test.html------------------------------
hello {{ d }}
The year is {{ d.year }}
The month is {{ d.month}}
#点语法也可以用来引用对象的方法。 例如,每个 Python 字符串都有 upper() 和 isdigit()
我们可以使用过滤器修改要显示的变量。Django提供了大约60个内置模板过滤器。您可以在内置过滤器参考中阅读有关它们的所有信息。在这里我们只介绍一些常用的模板过滤器.
过滤器 | 描述 |
---|---|
add | 将参数添加到值。如果value是4,那么输出将是6。此过滤器将首先尝试将两个值强制转换为整数。如果失败,它将尝试将值一起添加到一起。这将适用于某些数据类型(字符串,列表等),而对其他数据类型则失败。如果失败,结果将为空字符串。 |
capfirst | 大写值的第一个字符。如果第一个字符不是字母,则此过滤器无效。如果value是"django",输出将是"Django"。 |
cut | 从给定的字符串中删除arg的所有值。 |
date | 根据给定格式格式化日期。例:{{ value |
default | 如果value的计算结果为False,则使用给定的默认值。否则,使用该值。 |
dictsort | 获取字典列表并返回按参数中给出的键排序的列表。 |
join | 使用字符串连接列表,如Python str.join(list),如果value是列表,则输出将是字符串 。 |
filesizeformat | 格式,如一个“人类可读”的文件大小的值(如’13 KB’‘4.1 MB’‘102 bytes’) .例:如果value是123456789,则输出为117.7 MB |
first | 返回列表中的第一个项目 |
last | 返回列表中的最后一项。 |
length | 返回值的长度。这适用于字符串和列表。 |
lower | 将字符串转换为全部小写。 |
safe | 将字符串标记为在输出之前不需要进一步的HTML转义 |
slice | 返回列表的切片。使用与Python列表切片相同的语法。 |
striptags | 尽一切努力去除所有[X] HTML标记。请注意,striptags它不保证其输出是HTML安全的,特别是对于无效的HTML输入。因此,永远不要将 safe过滤器应用于striptags输出。如果您正在寻找更强大的东西,您可以使用bleachPython库,特别是它的 干净方法。 |
实例:
#value1="abcSD"
{{ value1|upper }}
{{ value1|lower }}
#value2 =5
{{ value2|add:3 }}
#value3='he llo wo r ld'
{{ value3|cut:' ' }}
#import datetime
#value4=datetime.datetime.now()
{{ value4|date:'Y-m-d' }}
如果value的计算结果为False,则使用给定的默认值。否则,使用该值。
#value5=[]
{{ value5|default:'nothing' }}
#value6='跳转'
{{ value6 }}
{% autoescape off %} {#注意:{% 这两个符号之间不能有空格 #}
{{ value6 }}
{% endautoescape %}
{{ value6|safe }}
{{ value6|striptags }}
#value7='1234'
{{ value7|filesizeformat }}
{{ value7|first }}
{{ value7|length }}
{{ value7|slice:":-1" }}
#value8='http://www.baidu.com/?a=1&b=3'
{{ value8|urlencode }}
value9='hello I am yuan'
模板标签都是放在{% %}括号里的,常见的模板标签有{% load xxxx %}, {% block xxxx %}, {% if xxx %}, {% url ‘xxxx’ %}。这些模板标签的本质也是函数,标签名一般即为函数名。这些标签的主要作用包括载入代码渲染模板或对传递过来的参数进行一定的逻辑判断或计算后返回。
Django模板标签(tags)的分类:
simple_tag (简单标签) : 处理数据,返回一个字符串或者给context设置或添加变量。
inclusion_tag (包含标签) : 处理数据,返回一个渲染过的模板。
熟悉Django的都知道,我们一般在视图view里设置context,然后通过它来传递数据给模板。 一个context是一系列变量和它们值的集合。通过使用simple_tag, 我们可以在视图外给context设置或添加变量。注: Django 1.9以后不再支持assignment_tag了,均使用simple_tag。
标签 | 描述 |
---|---|
autoescape | 控制当前的自动转义行为。此标记采用on或 off作为参数,并确定自动转义是否在块内生效。该块以endautoescape结束标记关闭。当自动转义生效时,所有变量内容都会在将结果放入输出之前对其应用HTML转义,这相当于手动将escape 过滤器应用于每个变量。 |
comment | 在{% comment %}和{% endcomment %}之间的内容会被忽略,作为注释。比如,当要注释掉一些代码时,可以用此来记录代码被注释掉的原因。 |
firstof | 输出第一个不是的参数变量False。如果所有传递的变量都没有输出False。 |
for | 循环遍历数组中的每个项目,使项目在上下文变量中可用。 |
if | 所述标签计算一个变量,并且如果该变量是“真”(即存在,不为空,并且不是假布尔值)的数据块的内容被输出,并且一定以endif作为结束 |
csrf_token | 用于生成csrf_token的标签,用于防治跨站攻击验证。注意如果你在view的index里用的是render_to_response方法,不会生效其实,这里是会生成一个input标签,和其他表单标签一起提交给后台的。 |
url | 引用路由配置的地址 |
with | 用更简单的变量名替代复杂的变量名 |
if标签的使用
----------------------------------------------test_if.html------------------------------------------------------
if_test
{% if jerry.age == 20 %}
{{ jerry.name }}的年龄为{{ jerry.age }}
{% elif jerry.age == 18 %}
{{ jerry.name }}的年龄不是20,而是{{ jerry.age }}
{% else %}
不知道{{ jerry.name }}的年龄
{% endif %}
{% comment %}
{% if %} 标签接受and,or或者not来测试多个变量值或者否定一个给定的变量
允许在同一标记中使用both and和or子句, and优先级高于or例如:
{% if athlete_list and coach_list or cheerleader_list %}
将被解释为:if (athlete_list and coach_list) or cheerleader_list
在if标记中使用实际括号是无效的语法。如果需要它们来指示优先级,则应使用嵌套if标记。
{% endcomment %}
----------------------------------------views.py------------------------------------------------------
def test_if(request):
jerry = {"name" : "Jerry","age" : 18}
return render(request, "test_if.html", locals())
----------------------------------------urls.py-------------------------------------------------------
path('test_if/', views.test_if),
for标签的使用
forloop.counter | 循环的当前迭代(1索引) |
forloop.counter0 | 循环的当前迭代(0索引) |
forloop.revcounter | 循环结束时的迭代次数(1索引),也就是倒序 ,最后一个序号为1 |
forloop.revcounter0 | 循环结束时的迭代次数(0索引)也就是倒序 |
forloop.first | 如果这是第一次通过循环,则为真 |
forloop.last | 如果这是最后一次循环,则为真 |
forloop.parentloop | 对于嵌套循环,这是围绕当前循环的循环 |
-------------------------------------------test_for.html--------------------------------------------
forloop.counter的效果:
{% for index in for_list %}
{{ forloop.counter }}:{{ index }}
{{ forloop.revcounter }}:{{ index }}
{% endfor %}
{% comment %}
#在标签里添加reversed来反序循环列表:
{% for obj in test_list reversed %}
...
{% endfor %}
{% endcomment %}
----------------------------------------views.py---------------------------------------------------
def test_for(request):
for_dict ={"name" : "Jerry","age" : 18}
for_list = ["first","second","third"]
return render(request, "test_for.html", locals())
----------------------------------------------------------------------------------------------------
变量forloop.first当第一次循环时值为True,在特别情况下很有用:
{% for object in objects %}
{% if forloop.first %}{% else %} {% endif %}
{{ object }}
{% endfor %}
# 富有魔力的forloop变量只能在循环中得到,当模板解析器到达{% endfor %}时forloop就消失了
# 如果你的模板context已经包含一个叫forloop的变量,Django会用{% for %}标签替代它
# Django会在for标签的块中覆盖你定义的forloop变量的值
# 在其他非循环的地方,你的forloop变量仍然可用
for...... empty
该for标签可以使用一个可选条款,其文本显示,如果给定的数组为空或无法找到,则会显示标签{% empty %}内的内容,并且for和empty标签属于并列关系,empty标签在for和endfor内.
#{% empty %}
{{li }}
{% for i in li %}
{{ forloop.counter0 }}----{{ i }}
{% empty %}
this is empty!
{% endfor %}
# [11, 22, 33, 44, 55]
# 0----11
# 1----22
# 2----33
# 3----44
# 4----55
Django模板引擎中最强大的 - 也是最复杂的 - 是模板继承。模板继承允许您构建一个基础“骨架”模板,其中包含站点的所有常用元素,并定义子模板可以覆盖的块。
通过以示例开头,最容易理解模板继承:
{% block title %}My amazing site{% endblock %}
{% block content %}{% endblock %}
我们将调用此模板,base.html定义一个简单的HTML框架文档,您可以将其用于简单的双列页面。“子”模板的工作是用内容填充空块。
在此示例中,block标记定义了子模板可以填充的三个块。所有block标记都是告诉模板引擎子模板可以覆盖模板的那些部分。
子模板可能如下所示:
{% extends "base.html" %}
{% block title %}My amazing blog{% endblock %}
{% block content %}
{% for entry in blog_entries %}
{{ entry.title }}
{{ entry.body }}
{% endfor %}
{% endblock %}
该extends标签是这里的关键。它告诉模板引擎该模板“扩展”另一个模板。当模板系统评估此模板时,首先它找到父模板 - 在本例中为“base.html”。
此时,模板引擎会注意到三个block标签,base.html并用子模板的内容替换这些块。根据值blog_entries,输出可能如下所示:
My amazing blog
Entry one
This is my first entry.
Entry two
This is my second entry.
请注意,由于子模板未定义sidebar块,因此将使用父模板中的值。 父模板中标记内的内容始终用作后备。{% block %}
您可以根据需要使用尽可能多的继承级别。使用继承的一种常见方法是以下三级方法:
这种方法可以最大化代码重用,并且可以轻松地将项目添加到共享内容区域,例如部分范围的导航。
以下是使用继承的一些提示:
如果在模板中使用,则它必须是该模板中的第一个模板标记。否则,模板继承将不起作用。{% extends %}
基础模板中的更多标签更好。请记住,子模板不必定义所有父块,因此您可以在多个块中填写合理的默认值,然后仅定义稍后需要的块。最好有更多的钩子而不是更少的钩子。{% block %}
如果您发现自己在许多模板中复制了内容,则可能意味着您应该将该内容移动到父模板中。{% block %}
如果您需要从父模板获取块的内容,该变量将起作用。如果要添加到父块的内容而不是完全覆盖它,这将非常有用。使用的数据不会自动转义(请参阅下一节),因为必要时,它已在父模板中转义。{{ block.super }}{{ block.super }}
使用模板标记语法在a之外创建的变量不能在块内使用。例如,此模板不呈现任何内容:{% block %}as
{% trans "Title" as title %}
{% block content %}{{ title }}{% endblock %}
{% block content %}
...
{% endblock content %}
在较大的模板中,此技术可帮助您查看 正在关闭的标记。{% block %}
最后,请注意,您无法block在同一模板中定义多个具有相同名称的标记。存在这种限制是因为块标签在“两个”方向上工作。也就是说,块标记不仅提供填充孔 - 它还定义填充父级孔的内容。如果block模板中有两个类似命名的标记,则该模板的父级将不知道要使用哪个块的内容。
参考Django文档