Django模板系统
常用语法
Django模板中只需要记两种特殊符号:
{{ }}和 {% %}
{{ }}表示变量,在模板渲染的时候替换成值,{% %}(表示标签)表示逻辑相关的操作。
变量
{{ 变量名 }}
变量名由字母数字和下划线组成。
点(.)在模板语言中有特殊的含义,用来获取对象的相应属性值。
几个例子:
view中代码:
def template_test(request):
li = [11, 22, 33]
dic = {"name": "mcw"}
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
def dream(self):
return "{} is dream...".format(self.name)
ma = Person(name="ma", age=18)
hong = Person(name="hong", age=9000)
ming = Person(name="ming", age=18)
person_list = [ma,hong,ming]
return render(request, "template_test.html", {"li": li, "dic": dic, "person_list": person_list}) #传字符串,数字,列表,字典等
模板中支持的写法:
<p>
取li中的第一个参数 :<br>
{{ li.0 }} <br> {# 列表.索引,而不是列表[索引] #}
取字典中key的值: <br>
{{ dic.name }} <br> {# 字典.键 而不是字典['键'] #}
取对象的name属性(对象变量),可以连着取 :<br>
{{ person_list.0.name }} <br> {# person_list.0是ma对象,对象.name取实例变量 #}
操作只能调用不带参数的方法 :<br>
{{ person_list.0.dream }}<br> {# person_list.0是ma 对象,对象.方法(而不是对象.方法())调用不带参数的方法#}
</p>
也可以这样多行传递并渲染:render拿到模板文件和变量并将模板文件替换然后发送给浏览器,是python做的渲染
字符串 数字 列表 字典
列表索引不能用负数取值,报错:
取值:
字典中的值也可以点下去,get方法不能用。类的返回值不好看,可以用str方法
取字典中的数据:支持keys values items
注:当模板系统遇到一个(.)时,会按照如下的顺序去查询:
- 在字典中查询
- 属性或者方法
- 数字索引
现在字典中找,而不是先使用keys方法
Filters
翻译为过滤器,用来修改变量的显示结果。
语法: {{ value|filter_name:参数 }}
':'左右没有空格没有空格没有空格
有空格报错:
default
{{ value|default:"nothing"}}
如果value值没传的话就显示nothing
没传值或者布尔值为False的似乎都是显示nothing,相当于没传值
注:TEMPLATES的OPTIONS可以增加一个选项:string_if_invalid:'找不到',可以替代default的的作用。
filesizeformat
将值格式化为一个 “人类可读的” 文件尺寸 (例如 '13 KB', '4.1 MB', '102 bytes', 等等)。例如:
{{ value|filesizeformat }}
如果 value 是 123456789,输出将会是 117.7 MB。
add
给变量加参数
{{ value|add:"2" }}
value是数字4,则输出结果为6。
{{ first|add:second }}
如果first是 [1,.2,3] ,second是 [4,5,6] ,那输出结果是 [1,2,3,4,5,6] 。
数字和字符串类型的数字使用add,怎么拼得到的都是数字运算的结果;数字和非数字字符串使用add方法似乎没有结果;非数字字符串和非数字字符串,数字字符串和非数字字符串都是字符串拼接
lower
小写
{{ value|lower }}
upper
大写
{{ value|upper}}
title
标题
{{ value|title }}
ljust
左对齐
"{{ value|ljust:"10" }}"
rjust
右对齐
"{{ value|rjust:"10" }}"
center
居中
"{{ value|center:"15" }}"
length
{{ value|length }}
返回value的长度,如 value=['a', 'b', 'c', 'd']的话,就显示4.
slice
切片
{{value|slice:"2:-1"}}
first
取第一个元素
{{ value|first }}
last
取最后一个元素
{{ value|last }}
join
使用字符串拼接列表。同python的str.join(list)。
{{ value|join:" // " }}
truncatechars
如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾。 truncatewords
参数:截断的字符数
{{ value|truncatechars:9}}
date
日期格式化
{{ value|date:"Y-m-d H:i:s"}}
可格式化输出的字符:点击查看。 没有% M->i S->s
或者修改配置
USE_L10N = False
DATETIME_FORMAT="Y-m-d H:i:s"
#还有DATA_FORMAT TIME_FORMAT
safe
Django的模板中会对HTML标签和JS等语法标签进行自动转义,原因显而易见,这样是为了安全。但是有的时候我们可能不希望这些HTML元素被转义,比如我们做一个内容管理系统,后台添加的文章中是经过修饰的,这些修饰可能是通过一个类似于FCKeditor编辑加注了HTML修饰符的文本,如果自动转义的话显示的就是保护HTML标签的源文件。为了在Django中关闭HTML的自动转义有两种方式,如果是一个单独的变量我们可以通过过滤器“|safe”的方式告诉Django这段代码是安全的不必转义。 转义是防止XSS攻击的
比如:
value = "<a href='#'>点我</a>"
{{ value|safe}}
如果博客评论里面可以将js插入到程序里面,然后这是一个循环一万次的alert,那么总是进不了网页,而在页面总是弹出alert框,直到循环结束。某些网站就是这样,陷入死循环退不出去浏览器,只能重启浏览器了
为了安全起见,会变成字符串展示在页面,如果想让它变成程序使用,添加 safe
没有safe的是被转义过了的,所以在页面不是a标签显示出来,而是纯文本显示。
如果传进的有safe,那么safe前面传进的是不断循环alert,那么这个网页就会执行很多此次alert而进不了网页。它的作用就是告诉django这里不需要转义
自定义filter
自定义过滤器只是带有一个或两个参数的Python函数:
- 变量(输入)的值 - -不一定是一个字符串
- 参数的值 - 这可以有一个默认值,或完全省略
例如,在过滤器{{var | foo:“bar”}}中,过滤器foo将传递变量var和参数“bar”。
自定义filter代码文件摆放位置:
app01/ __init__.py models.py templatetags/ # 在app01下面新建一个package package __init__.py app01_filters.py # 建一个存放自定义filter的py文件 views.py
编写自定义filter
from django import template register = template.Library() @register.filter def fill(value, arg): return value.replace(" ", arg) @register.filter(name="addSB") def add_sb(value): return "{} SB".format(value)
使用自定义filter
{# 先导入我们自定义filter那个文件 #} {% load app01_filters %} {# 使用我们自定义的filter #} {{ somevariable|fill:"__" }} {{ d.name|addSB }}
没有导入自定义过滤器:导入模块,直接使用app下模块中的函数
已导入,还是报错:'my_tags' is not a registered tag library. Must be one of:
遇到这个,第一步重启项目。报错变了
原因,L应该大写,Library()
实现替换:
命名错误,在python这个包的init文件里随便写点东西,报错。
这里名称必须正确且与这个一致,写错会报错:
手动创建的一个目录而不是在pycharm中创建的包也行,但是没有init文件,可以补充一个,不过尽量还是创建包好些;
from django import template registers=template.Library() @registers.filter def fill(value, arg): #最多两个参数,一个过滤器的变量,一个你想要的参数 return value.replace(" ", arg) #将渲染值中的空格替换为你想要的arg @registers.filter(name="addSB") def add_sb(value): #给要渲染的值做一个操作,并给过滤器函数起个名字 return "{} SB".format(value) #第二个传参要不要都行。将传进来做渲染的数据经过自定义过滤器做一次操作之后
过滤器另取个名之后,函数名就无效了:
改成新起的名字就好了:
Tags
for
根据多个值,对每个值做不同的操作
<ul> {% for user in user_list %} <li>{{ user.name }}</li> {% endfor %} </ul>
for循环可用的一些参数:
flask中使用jinja2
django中使用下面的方式,有些区别
Variable | Description |
---|---|
forloop.counter |
当前循环的索引值(从1开始) |
forloop.counter0 |
当前循环的索引值(从0开始) |
forloop.revcounter |
当前循环的倒序索引值(到1结束) |
forloop.revcounter0 |
当前循环的倒序索引值(到0结束) |
forloop.first |
当前循环是不是第一次循环(布尔值) |
forloop.last |
当前循环是不是最后一次循环(布尔值) |
forloop.parentloop |
本层循环的外层循环 |
当前循环的索引值(从0开始)
循环计数倒叙,但不影响列表取值顺序:
倒叙循环计数,索引最值小为0 当前循环的倒序索引值(到0结束)
当前循环是不是第一次循环(布尔值) 当前循环是不是最后一次循环(布尔值)
如果只有一个元素,那么既是第一个也是最后一个
如果只是打印forloop那么显示的是一个字典,字典里有刚才的那些变量,然后.出来值,还有一个就是parentloop循环是空值,因为这里没有使用外层循环
现在传进去是一个列表里面套着几个列表:取出来每个元素都是列表;
现在展示列表里面的内容,需要再来一次内层循环:
每一行都是一个外层循环,一行中的每列都是在内层循环。一行中的每列的parentloop都是相同的,是同一个外层循环,prentloop的rarentloop没有值,因为父的循环往上就没有循环了
第二行就是循环计数第二个的外层循环,然后当前外层循环下进行内层循环展示每列的内容
last和first是只的是每个for循环的
将表格偶数列变成红色字体:如果循环计数是偶数,那么在内存循环中给这列加上颜色
如果想要实现偶数行并且是偶数列的单元格是红色字:那么加一个and共判断两个条件。外层定义行,内层定义了列。内层中获取它的循环序数,也能获取外层循环的序号;
偶数行和偶数列才变红:内层循环判断,满足循环序数是偶数且父循环序数也是偶数才添加样式
for ... empty
<ul> {% for user in user_list %} <li>{{ user.name }}</li> {% empty %} <li>空空如也</li> {% endfor %} </ul>
<table border="1"> <thead> <tr> <th>序号</th> <th>ID</th> <th>书名</th> <th>出版社</th> <th>操作</th> </tr> </thead> <tbody> {% for book in books %} <tr> <td>{{ forloop.counter }}</td> <td>{{ book.pk }}</td> <td>{{ book.title}}</td> <td>{{ book.pub}}</td> <td><a href="/del_book/?pk={{ book.pk }}">删除</a> <a href="/edit_book/?pk={{ book.pk }}">编辑</a> </td> </tr> {% empty %} <td colspan="5" style="text-align: center;">没有相关的数据</td> {% endfor %} </tbody>
将数据都删除
如果数据库中没有查询到数据,这样显示不友好:
当显示为空的时候给用户一个提示,而不是误以为有问题:
再让它居中一下。合并一下。
if,elif和else
{% if user_list %} 用户人数:{{ user_list|length }} {% elif black_list %} 黑名单数:{{ black_list|length }} {% else %} 没有用户 {% endif %}
当然也可以只有if和else
{% if user_list|length > 5 %} 七座豪华SUV {% else %} 黄包车 {% endif %}
if语句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判断。
pycharm中连续比较 真:比较原理是 10>5是真,5>1是真,两次比较结果再and做个比较,结果是真
控制台中连续比较 假:比较原理是10>5是true, true>1比较是false。因此两种比较方式不同,结果不同
如下在js控制台中就显示true了,10>5是true ,true==1 结果就是true
模板语言和js是相同的,连续大于要空格,虽然报红但是不影响结果显示
不能使用1+2==判断,而是要用过滤器计算然后判断是否==
with
定义一个中间变量。(定义变量,起别名)
{% with total=business.employees.count %} {{ total }} employee{{ total|pluralize }} {% endwith %}
{% with business.employees.count as total%} {{ total }} employee{{ total|pluralize }} {% endwith %}
hobby_list.0.2
内容太长或者是列表中连续索引取值而不能清楚取值是什么,这时候用 with起个别名
csrf_token
这个标签用于跨站请求伪造保护。
CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。
在页面的form表单里面写上{% csrf_token %}
跨站请求的理解:
用户 a 服务器b 服务器c
从a到b请求,b返回a中可能有form表单。表单中action提交的地址经常不写,不写默认是提交到当前的地址。如果form表单中的地址是另一个网站的地址(服务器c),这样你的post请求就不是服务器b,而是去请求服务器c另一个网站的了,这就叫跨站请求。对于服务器c来说它没有做任何校验,任何人都能对它进行post请求
对于咱们来说应该是从a请求c,获取到页面,然后填写账号密码以及转账的用户和金额,填写完后点击提交post请求进行转账。如果你是不小心点击广告等进入的是钓鱼网站服务器b等等,这样你就说从b中获取的转账提交网页,钓鱼网站的form表单中没有填写action,那么post请求的是钓鱼网站b,但是服务器B中的网站写了c的ation。那么你做的是c的post转账请求,看着是一模一样的是提交到c的,但是如果b的请求网页中内部做了一些修改将你的转账账号修改成了他人的账号,那么你就转账到了非法账户中了,这客户就产生了损失。c不能随便就能提交post请求的,需要校验,不认可从别人的服务器form表单提交过来的请求。正常的应该是从a请求c获取的form标签再提交到c。那么应该怎样区别出是c自己的form提交还是b跨站来的form表单提交给b呢?那么服务器c就要做个标记,用户获取到它的网页并用这个网页提交post请求那么会把标记提交回来。如果是从b获取的form表单页面那么a是没有c给的标记,这样a写好form表单之后再提交到c那么就校验不通过了。
假如a是从b拿到的请求,b返回a的页面。这时a又从c出获得了请求c需要携带的标记,再去往c发送请求,这样c也就认可它了。这就是跨站请求伪造,a从c通过csrf从c获取到标记然后渲染到a从c获取的form表单页面,这样c处校验跨站请求就能通过,否则不能通过校验就不能用b的form表单post提交到action是c地址的c服务器
在配置中把这个注释去掉:
这下post请求就会被拒绝了。因为当前服务器还不能确定是谁的网页发送的post请求
所有要想自己发出去的页面中提交的post请求能通过校验,那么要在发送出去的网页中添加标签 {% csrf_token %},标签写在form表单外的时候如下。
这样网页并没有显示什么不同。但是网页中有了一个{% csrf_token %}渲染出来的类型是隐藏的input标签,名字是csrf中间件token,值是一串字符
这样的话我要请求,就可以拿到下面三个键值对,其中包括csrf的token。
标签写在form表单外面get能渲染token进模板,但是post没有获取到token
将它放在form表单中才能在post请求获取到csrf中间件的token值,才能在服务器端根据这个值做校验,校验成功才能正常访问,否则403拒绝
注释
{# ... #} #这个注释不做渲染
<!-- {{可以使用渲染的内容}} --> #这个注释做渲染,但是不在浏览器上展示出来,在开发工具的元素中可以看到它是存在的,它作为代码提示用的
注意事项
1. Django的模板语言不支持连续判断,即不支持以下写法:
{% if a > b > c %} ... {% endif %}
2. Django的模板语言中属性的优先级大于方法
def xx(request): d = {"a": 1, "b": 2, "c": 3, "items": "100"} return render(request, "xx.html", {"data": d})
如上,我们在使用render方法渲染一个页面的时候,传的字典d有一个key是items并且还有默认的 d.items() 方法,此时在模板语言中:
{{ data.items }}
默认会取d的items key的值。
母板
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Title</title> {% block page-css %} {% endblock %} </head> <body> <h1>这是母板的标题</h1> {% block page-main %} {% endblock %} <h1>母板底部内容</h1> {% block page-js %} {% endblock %} </body> </html>
注意:我们通常会在母板中定义页面专用的CSS块和JS块,方便子页面替换。
下面的书籍列表页面:
和下面的页面可以做成一致的,也需要用下面的出版社样式,如果只是复制粘贴使用共有展示的东西的话,可能复制出问题,而且多个页面中含有的重复的代码特别多。并且一个页面做了公共部分的修改了,那么每个页面都要做修改,这是代码重复的问题,那么代码重复需要一直复制粘贴很多次,python中减少代码重复是写函数和类。父类里面写个属性,方法,用的时候子类继承这个父类,每个子类想用不同的方法和属性那么重新定义,覆盖掉父类中相同的方法和属性。这是继承,继承在模板页面也是类似的
那么1处,2处的公共的部分需要分出来作为模板 ,而3处作为变化的要替换的内容;
下面就创建基础模板,并删掉上面那个网页中多余出来的变化的部分:
先把我们需要的模板访问展示出来,1处就是变化的部分,其它部分是公共的功能,已经把公共的部分提出了了。
在你删掉的那部分代码处添加block块:它就相当于公共的类和方法。
创建母版的思想就是将页面公共的地方提取出来,将需要子页面都需要添加内容的地方添加block块,然后子页面中用代码将这个block块替换掉
publisher_list.html这个页面中将公共的部分删掉,并将刚才删掉的部分留下,导入base.html母版,删掉的部分作为变化的替换掉上面名称为content的block块。
<html lang="en"> <head> <meta charset="UTF-8"> <title>魔降风云变</title> <link rel="stylesheet" href="/static/plugins/bootstrap-3.3.7-dist/css/bootstrap.css"> <link href="/static/css/dashboard.css" rel="stylesheet"> </head> {#<body>#} <body> <nav class="navbar navbar-inverse navbar-fixed-top"> <div class="container-fluid"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">图书管理系统</a> </div> <div id="navbar" class="navbar-collapse collapse"> <ul class="nav navbar-nav navbar-right"> <li><a href="#">Dashboard</a></li> <li><a href="#">Settings</a></li> <li><a href="#">Profile</a></li> <li><a href="#">Help</a></li> </ul> <form class="navbar-form navbar-right"> <input type="text" class="form-control" placeholder="Search..."> </form> </div> </div> </nav> <div class="container-fluid"> <div class="row"> <div class="col-sm-3 col-md-2 sidebar"> <ul class="nav nav-sidebar"> <li class="active"><a href="/publisher_list/">出版社列表 <span class="sr-only">(current)</span></a></li> <li><a href="#">Reports</a></li> </ul> </div> <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main"> <div class="panel panel-primary"> <div class="panel-heading"> <h3 class="panel-title">出版社列表</h3> </div> <div class="panel-body"> <a class="btn btn-success btn-sm" href="/add_publisher">新增</a> <div class="table-responsive"> <table class="table table-striped"> <thead> <tr> <th>序号</th> <th>ID</th> <th>名称</th> <th>操作</th> </tr> </thead> <tbody> {% for publisher in all_publishers %} <tr> <td>{{ forloop.counter }}</td> <td>{{ publisher.pk }}</td> <td>{{ publisher.name }}</td> <td> <a class="btn btn-danger btn-sm" href="/del_publisher/?id={{ publisher.pk }}">删除</a> <a class="btn btn-success btn-sm" href="/edit_publisher/?id={{ publisher.pk }}">编辑</a> </td> </tr> {% endfor %} </tbody> </table> </div> </div> </div> </div> </div> </div> </body> </html>
/publisher_list/ 导入模板成功显示公共功能部分
在publisher.html页面中再添加模板中可以替换的block块content部分。在子html中将刚才删除的内容添加进去:
然后子页面就使用继承的方法正常显示了
下面把下面的book_list.html页面也修改成这个模板的。
这里只显示模板没有显示book的数据,因为之前这里函数改成了模板页面了,改回来
book页面也用这个模板渲染出来了:
将表格样式用publisher_list.html中的样式修改一下:
左边书籍列表添加要在base母版修改添加:
虽然实现了,但是都是被选中状态,因为这两个标签都有active类
如果没有这个类是未被选中状态:
那么就用block块的方法代替它 ,点击哪个页面的时候先将模板所有的变成空,然后将点击的要显示这个html添加active类
重名了:
换个名字,现在都是未被选中:
给这里默认填充一个内容让它被选中:
然后在子中替换掉:实现点击谁只有谁被选中并展示出来
我也可以直接用publisher.html做母版,将变化的内容写在block里面,然后子继承父,子中将对应block内容重写替换掉父的。拿一个子类当成父类,其它继承这个父类。我们要做的就是把页面中特殊的替换掉。用base或者某个页面做母版看情况而定
总结:
就是一个普通HTML提取多个页面的公共部分 定义block块
继承:
-
{% extends ‘base.html’ %}
-
重写block块 —— 写子页面独特的内容
注意的点:
-
{% extends 'base.html' %} 写在第一行 前面不要有内容 有内容会显示
-
{% extends 'base.html' %} 'base.html' 加上引号 不然当做变量去查找
-
把要显示的内容写在block块中
-
写在第一行母版之前:1、{% extends 'base.html' %} 写在第一行 前面不要有内容 有内容会显示
写在块的外面;{% extends 'base.html' %} 'base.html' 加上引号 不然当做变量去查找
把继承中的模板去掉引号:它会把它当做变量(或者变量没有正确获取)并报错:Invalid template name in 'extends' tag: ''. Got this from the 'base.html' variable.
那么将母版以变量的形式从函数中传进来并在子中继承:以后想要灵活点,可以换模板那么就将母版以变量的形式传递进来。
注意:我们有的时候想要子页面有不同的样式或js,而不是只能使用父的,因此我们通常会在母板中定义页面专用的CSS块和JS块,方便子页面替换。
{% block page-css %} {% endblock %}
<h1>母板底部内容</h1> {% block page-js %} {% endblock %}
继承母板
在子页面中在页面最上方使用下面的语法来继承母板。
{% extends 'layouts.html' %}
块(block)
通过在母板中使用{% block xxx %}
来定义"块"。
在子页面中通过定义母板中的block名来对应替换母板中相应的内容。
{% block page-main %} <p>世情薄</p> <p>人情恶</p> <p>雨送黄昏花易落</p> {% endblock %}
组件
可以将常用的页面内容如导航条,页尾信息等组件保存在单独的文件中,然后在需要使用的地方按如下语法导入即可。
组件就是实现一角的功能的一小段代码块
{% include 'navbar.html' %}
那么这里也可以使用模板和继承,那么我把base的左边框和内容部分替换掉,这样的话要替换的比较多。 更方便的是把导航栏做成一个组件,将它导入到新增页面,就像Python里的模块,想用导入就行了。
现在把母版里的导航栏剪切下来:
剪切下来继承使用它的就没有这个导航栏了,剪切下来放到一个html文件
但是页面还是要使用导航栏的功能,导入这个组件即可,这就是一部分代码块(放到里面就能使用展示想要效果的代码块;将一个功能剪切出去放到html文件就实现了一个组件,这个代码块就是组件)
然后直接导入组件 include 加组件文件名字
{% include 'nav.html' %}
如下就将之前想要添加的部分功能添加到这个页面了:因为多个页面都是导入的这一个组件,那么只要修改这个组件,比如把图书管理系统名字改掉,只需要在组件 中修改就可以把所有用这个组件的页面显示都修改成新的名字了。
一小段代码块
静态文件相关
如果将来部署的过程当中,静态文件别名想要做个变更,不想用这个名字了,那么换了名字之后就这样了:
1处修改,2和3的引用使用的固定的没有修改这样4处样式就没有使用上了
并且这里报错找不到,因为别名修改了而引用路径没有修改。引用必须和别名一致才能找到样式。那么如果手动修改引用的别名的话,修改那么多个肯定不方便
那么把这个别名的引用变成变量的那种形式来使用,让它自己去找这个别名然后替换成当前配置的别名;
首先导入,然后使用,如果没有传参会报错至少一个传参,开头有没有/都可以:
它会给你做上拼接:
staticfiles也可以的
导入static使用这个tag那么获取到的是静态目录的那个别名:
使用它来的话这里不能加/,就是获取别名然后渲染上了,不会给你添加/
{% load static %} <img src="{% static "images/hi.jpg" %}" alt="Hi!" />
引用JS文件时使用:
{% load static %} <script src="{% static "mytest.js" %}"></script>
某个文件多处被用到可以存为一个变量
{% load static %} {% static "images/hi.jpg" as myphoto %} <img src="{{ myphoto }}"></img>
使用get_static_prefix
{% load static %} <img src="{% get_static_prefix %}images/hi.jpg" alt="Hi!" />
或者
{% load static %} {% get_static_prefix as STATIC_PREFIX %} <img src="{{ STATIC_PREFIX }}images/hi.jpg" alt="Hi!" /> <img src="{{ STATIC_PREFIX }}images/hi2.jpg" alt="Hello!" />
自定义simpletag
和自定义filter类似,只不过接收更灵活的参数。
定义注册simple tag 。simpletag前面和filter相似,就是使用不同的装饰器传递的参数数量不同
@register.simple_tag(name="plus") def plus(a, b, c): return "{} + {} + {}".format(a, b, c)
使用自定义simple tag
{% load app01_demo %} {# simple tag #} {% plus "1" "2" "abc" %}
inclusion_tag
多用于返回html代码片段
组件也是一个代码段,与组件的区别是组件是固定的,而这个是灵活的可以传变量进去先进行渲染,然后再在使用这个标签的地方替换成这个被变量渲染过的代码段(导入)
示例:
templatetags/my_inclusion.py
from django import template register = template.Library() @register.inclusion_tag('result.html') def show_results(n): n = 1 if n < 1 else int(n) data = ["第{}项".format(i) for i in range(1, n+1)] return {"data": data}
templates/result.html
<ul> {% for choice in data %} <li>{{ choice }}</li> {% endfor %} </ul>
templates/index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>inclusion_tag test</title> </head> <body> {% load my_inclusion %} {% show_results 10 %} </body> </html>
使用下面的分页:
但是这个是固定死的页数,修改一下:
1)1处写函数,将参数传到函数中。返回一个字典。这里字典的一个值是1-10的列表吧,
2)1处将page函数返回值渲染page.html代码段中的内容形成一个代码段
3)3处导入这个模块,使用这个标签函数,并指定传参。这样引用并生成灵活可变动的page.html代码段,并在这个页面中渲染出来。
4)就相当于我生成一个可传参修改变动的组件,然后将组件导入到需要的页面。根据 需要产生细微差别
使用jinja2渲染生成html,与框架没有关联,只需要导入jinja2就行
可以用来生成邮件模板。有些数据不需要用web框架去展示,但是我们需要生成html页面源码,可以在别处直接打开源码页或者其它地方使用渲染后的页面。比如发送邮件html,让邮箱去用我们写的源码页面展示数据,比如生成html页面,将页面发送给其它需要看数据的人,让他们无需联网,直接本地查看。数据采集可以是来自主机命令的信息收集。
使用jinja2定义自己的邮件html模板并发送邮件:点击这里
参考链接:https://www.cnblogs.com/maple-shaw/articles/9333821.html