其实我接触过的web框架也就Java下的jsp和Python下的web.py和django、flask,但是他们都有类似模版语言的存在,有的是像Django一样专门的创造,有的是简单的在html文档里嵌入编程语句。但是毋庸置疑,这些“模版语言”是让html文件变得动态的根源。
模版语言定义(Django Template Language, DTL)
Django的模版语言十分简明,很容易就能学会。我们用几个判断句来给模版语言定义。
-1 所有使用了模版语言的html文件被我们称为模版。
-2 在模版语言中,{% 语句 %}、{{ 输出 }}、{# 注释 #}。也就是说前者括号内的内容会被django执行,后者直接被运算后写在文件的相应位置上。
-3 在模版语言中,我们的变量可以使用|(过滤器)和.运算符,我们的语句可以用来载入内容或使用for/if/block/extends/url等标签。不能直接运行python语言,这点要谨记。
{{ 输出 }}
1 View与Template的交互
模版由视图渲染并传给客户端,使用render函数。当模版中需要数据时,我们向render函数传入同键名的字典。render函数的介绍在上一部分。
2 .运算符
DTL中的点运算符按照字典->对象->列表/元组查询,当以上查询都不成立的时候返回空字符串。也就是说,如果你在前端传入了一个数组a=[1,2,…],前端模版内你对其的访问应为a.1、a.2…
例1 view传值:
示例项目根路径下urls.py里urlpatterns添加:
path("thirdurl",views.third)
子app helloworld下views.py:
def third(request):
if(request.method=="GET"):
dictionary={"key1":"1","key2":[1,2,3,4],"key3":{"a":"a","b":"b"}}
return render(request,"我的html.html",dictionary)
子app helloworld下文件夹templates下我的html.html:
<!doctype html>
<html>
<head></head>
<body>
{{key1}}
{{key2.0}}
{{key3.a}}
</body>
</html>
项目下打开命令行runserver在127.0.0.1:8000下运行django,输入127.0.0.1:8000/thirdurl,可以看到返回了1 1 a。
3 |运算符与过滤器
过滤器可以被视为专门用来处理模版数据的python函数,在模版中,我们通过|运算符使用过滤器。Django内置了很多过滤器,这些可以在网上搜到,我们也可以自己制作过滤器。
-过滤器在使用之前需要使用{% load 过滤器文件名 %}导入。
-过滤器文件默认放在app目录下templatetags文件夹下。
-过滤器文件中定义的函数即是过滤器,函数前要加上@register.filter(name="过滤器在模版中的名字"),整个文件前要加上两行:from django import template register=template.Library()。
例2 使用django自带的过滤器:
示例项目根路径下urls.py里urlpatterns添加:
path("fourthurl",views.fourth)
子app helloworld下views.py:
def fourth(request):
if(request.method=="GET"):
dictionary={"key1":"1"}
return render(request,"我的html.html",dictionary)
子app helloworld下文件夹templates下我的html.html:
<!doctype html>
<html>
<head></head>
<body>
{{key1.1|default:"不存在"}}
</body>
</html>
过滤器default为django自带的过滤器,功能为在返回空串时替代为后面的字符串。这个过滤器不需要导入(但是不是所有自带的过滤器都不要)。项目下打开命令行runserver在127.0.0.1:8000下运行django,输入127.0.0.1:8000/thirdurl,可以看到返回了“不存在”,因为key1.1找不到值。
例3 一个小例子,自建过滤器:
子app helloworld下文件夹templatetags文件夹下建立block1.py:
from django import template
register=template.Library()
@register.filter(name="_reverse")#这个过滤器翻转list
def _reverse(arg):
return reversed(arg)
在模版文件中这么引用:
<!doctype html>
<html>
<head></head>
<body>
{% load block1 %}
{{arg1|_reverse}}
</body>
</html>
{% 语句 %}
1 load语句
load语句用来载入自定义标签或过滤器文件,比如大名鼎鼎的{% load static %}。
# load static与 static ‘’
{% load static %}载入了static标签,{% static ‘文件路径’ %}用于加载静态文件。
很多人把static标签说的玄之又玄,其实不然。在没有额外设置的情况下,django会去app目录下的static目录下寻找static引号里字符串为路径的文件。(关于这些静态文件存放请参考我之前讲的模版文件的存放,请仔细思考django寻找文件的顺序和文件的储存顺序,如果你直接随意存储文件,当其重名时django只会返回第一个查找的,这里没有对于app的就近原则)
-如果想要修改static目录的名字,请修改settings.py文件下的STATIC_URL。(根据url的解析规则你应该能意会这个变量定义了什么)
-如果你的static不想放在app目录下,可以在settings.py文件下添加STATICFILES_DIRS变量,将其值设为你设定的static文件夹,例如STATICFILES_DIRS= (os.path.join(BASE_DIR, "static"),),则静态文件夹为项目目录下的static文件夹。
2 for语句和if语句
DTL中的for语句和if语句的使用与python中相同,但是书写方式有所不同。
{% for 表达式 %}内容{% endfor %} for语句
{% if 表达式 %}内容{% endif %} if语句
你可能注意到了两个语句之间我写的是内容而不是语句,事实上里面也可以写html标签。不要想当然。
3 block语句
block语句扩住的内容可以被覆写。我们通过引用模版并覆写其中的block完成模版代码的重用,如果不覆写块中的内容就是父模版中块的内容。(可以想象一个父类和重写父类方法的子类)这个特性可以很好的用于节省模版的编写时间,也可以让模版层次分明。
{% block 名称 %}内容{% endblock 名称 %} block语句
{% extends ‘模版文件名称’ %} 引用模版文件
例4 覆写block及引用static
项目app helloworld目录下模版文件夹下新建 父模版.html:
<!doctype html>
<html>
<head></head>
<body>
{% block myblock1 %}
<p>父内容</p>
{% endblock myblock1 %}
</body>
</html>
同目录新建 子模版.html:
{% extends ‘父模版.html’ %}
{% block myblock1 %}
<p>子内容</p>
{% endblock myblock1 %}
很明显,如果你的项目渲染了子模版,那么会出现“子内容”。
{# 注释 #}
关于这个注释只有一个需要注意的。Html文件的注释还会出现在文件内,只不过不被呈现出来,而DTL的注释在渲染模版的时候就会被剔除。
关于DTL的一些思考
相比于直接在模版文件中嵌入python语句,DTL显得更加高明。因为它不但简洁,而且从框架编写者的方面就严格遵循了MTV模式,不需要使用框架者特意去注意这些。这就像“带上脚镣的舞蹈”,是有限制的自由,但是也更美。
Tips:本文里涉及DTL的语句和标签是等价的。