61-Django进阶(模板系统)
1、模板系统
模板的实质就是用来替换对应的HTML元素,让网页“动”起来。
1.1 常用语法
与变量相关的用{{ }},与逻辑相关的用{% %}。注释用{# ... #}
模板中支持的写法:
1 {# 取list中的第一个参数 #} 2 {{ list.0 }} 3 {# 取字典中key的值 #} 4 {{ dict.name }} 5 {# 取对象的name属性 #} 6 {{ person_list.0.name }} 7 {# .操作只能调用不带参数的方法 #} 8 {{ person_list.0.dream }}
示例模板代码:
1 <hr> 2 {{ namehtml }} 3 4 <hr> 5 {{ agehtml }} 6 7 <hr> 8 9 <h3>模版之列表操作</h3> 10 {{ namelisthtml }} 11 <br /> 12 {{ namelisthtml.0 }} {# 取第一个元素 #} 13 {{ namelisthtml.1 }} {# 取第二个元素 #} 14 {{ namelisthtml.2 }} {# 取第三个元素 #} 15 {{ namelisthtml.3 }} {# 取第四个元素 #} 16 {{ namelisthtml.4 }} {# 取第五个元素 如果没有第五个元素,则代表取不到,不会在页面上显示#} 17 18 <h3>模版之字典操作</h3> 19 {{ namedicthtml }} 20 <br /> 21 {{ namedicthtml.first_name }} {# 取出key对应的value #} 22 {{ namedicthtml.last_name }} {# 取出key对应的value #} 23 24 <hr> 25 {{ p1html }} {# 得到的是对象p1的内存地址 #} 26 <br /> 27 {{ p2html }} {# 得到的是对象p2的内存地址 #} 28 <br /> 29 {{ p1html.name }} {# 得到对象p1的name属性 #} 30 {{ p1html.age }} {# 得到对象p1的age属性 #} 31 <br /> 32 {{ p2html.name }} {# 得到对象p2的name属性 #} 33 {{ p2html.age }} {# 得到对象p2的age属性 #} 34 <br /> 35 {{ p1html.speak }} {# 调用对象p1的方法(只能调用无参数的方法) #} 36 37 <hr> 38 {{ objlisthtml }} {# 获取所有对象 #} 39 <br /> 40 {{ objlisthtml.0 }} {# 获取第一个对象#} 41 <br /> 42 {{ objlisthtml.1 }} {# 获取第二个对象 #} 43 <br /> 44 {{ objlisthtml.0.name }} {# 获取对象的name属性,注意:.操作只能调用不带参数的方法 #} 45 {{ objlisthtml.0.age }} {# 获取对象的age属性 #} 46 <br /> 47 {{ objlisthtml.1.name }} {# 获取对象的name属性 #} 48 {{ objlisthtml.1.age }} {# 获取对象的age属性 #} 49 <br /> 50 {{ objlisthtml.0.speak }} {# 调用第一个对象的speak方法 #} 51 {{ objlisthtml.1.speak }} {# 调用第二个对象的speak方法 #}
与模板对应的后台代码:
1 def template_test(request): 2 name = "druid" 3 age = 18 4 p1 = Person("druid", 18) 5 p2 = Person("john", 19) 6 7 obj_list = [p1, p2] 8 9 return render(request, "tmp_test.html", 10 { 11 "namehtml": name, 12 "agehtml": age, 13 "namelisthtml": name_list, 14 "namedicthtml": name_dict, 15 "p1html": p1, 16 "p2html": p2, 17 "objlisthtml": obj_list, 18 })
1.2 filter
语法: {{ value|filter_name:参数 }} ("|"左右没有空格!)
示例模板代码:
1 {{ unknownhtml }} {# 如果没有给变量传递元素,则不会在页面上显示 #} 2 {{ unknownhtml|default:"没有给变量'unknownhtml'传值,使用自定义默认值" }} 3 4 <hr> 5 <p> 6 长度: 7 {{ namehtml|length }} {# 对象为字符串,则是字符串长度 #} 8 {{ namelisthtml|length }} {# 对象为列表,则是列表元素个数 #} 9 {{ namedicthtml|length }} {# 对象为字典,则是字典元素个数 #} 10 </p> 11 12 <hr> 13 <p>文件大小: 14 {{ filesizehtml1|filesizeformat }} {# 默认情况下,传递的值单位会被认为是bytes #} 15 {{ filesizehtml2|filesizeformat }} {# 默认情况下,传递的值单位会被认为是bytes #} 16 {{ filesizehtml3|filesizeformat }} {# 默认情况下,传递的值单位会被认为是bytes #} 17 {{ filesizehtml4|filesizeformat }} {# 默认情况下,传递的值单位会被认为是bytes #} 18 </p> 19 20 <hr> 21 <p>列表切片: 22 {{ namelisthtml|slice:"0:" }} {# 和列表切片一样,左取右不取 #} 23 {{ namelisthtml|slice:":-1" }} {# 取除了最后一位元素外的所有元素 #} 24 {{ namelisthtml|slice:"1:-1" }} {# 取第二位到除了最后一位的所有元素 #} 25 </p> 26 <p>字符串切片: 27 {{ namehtml|slice:"0:2" }} 28 {{ namedicthtml.first_name|slice:":-1" }} {# 和列表切片一样,左取右不取 #} 29 </p> 30 31 <hr> 32 <p> 33 未格式化之前的时间:{{ nowhtml }} 34 格式化之后的时间:{{ nowhtml|date:"Y-m-d H:i:s" }} 35 </p> 36 37 <hr> 38 默认情况下后端传递标签浏览器不会作为标签处理:{{ a_html }} 39 <br> 40 使用safe方法后浏览器会对标签进行处理:{{ a_html_safe|safe }} 41 <br> 42 攻击:{{ attackhtml }} {# 加了safe之后就会一直弹窗 #} 43 44 <hr> 45 <h5>{{ parastrhtml }}</h5> 46 <h5>{{ parastrhtml|truncatechars:32 }}</h5>
与模板对应的后台代码:
1 def template_test(request): 2 file_size1 = 1024 3 file_size2 = 1048576 4 file_size3 = 1073741824 5 file_size4 = 1099511627776 6 7 8 import datetime 9 now_time = datetime.datetime.now() 10 print(now_time) 11 12 a_tag = "<a href='http://www.baidu.com'>我是后端传过来的超链接,默认不会被浏览器作为a标签处理</a>" 13 a_tag_safe = "<a href='http://www.baidu.com'>我是后端传过来的超链接,使用safe方法后会被作为a标签处理</a>" 14 attack_script = "<script>for(1;;)alert('弹死你!');</script>" 15 16 para_str = """ 17 大段的文本: 18 《悲惨世界》是由法国作家维克多·雨果在1862年发表的一部长篇小说,其内容涵盖了拿破仑战争和之后的十几年的时间。 19 故事的主线围绕主人公土伦苦刑犯冉·阿让(Jean Valjean)的个人经历,融进了法国的历史、革命、战争、道德哲学、法律、正义、宗教信仰。 20 """ 21 22 return render(request, "tmp_test.html", 23 { 24 "namelisthtml": name_list, 25 "filesizehtml1": file_size1, 26 "filesizehtml2": file_size2, 27 "filesizehtml3": file_size3, 28 "filesizehtml4": file_size4, 29 "nowhtml": now_time, 30 "a_html": a_tag, 31 "a_html_safe": a_tag_safe, 32 "attackhtml": attack_script, 33 "parastrhtml": para_str, 34 })
1.3 自定义filter
在app文件夹下,创建templatetags包(名字固定),然后再新建myfilter.py文件作为自定义filter:
1 """ 2 该模块为自定义filter,文件夹的名字只能是templatetags 3 """ 4 from django import template 5 6 7 register = template.Library() # 生成注册用的实例,固定写法 8 9 10 @register.filter(name="sb") # 告诉Django的模版语言我现在有一个自定义的filter方法,名字叫sb。name="sb"这里相当于给add_sb取了个sb的别名。 11 def add_sb(arg): 12 return "{} is sb".format(arg) 13 14 15 @register.filter(name="self_str") # 告诉Django的模版语言我现在有一个自定义的filter方法,名字叫self_str。name="self_str"这里仍然相当于取别名。 16 def add_self_str(arg1, arg2): 17 """ 18 :param arg1: 第一个参数是管道符左边的变量 19 :param arg2: 第二个参数是冒号后面的那个变量 20 :return: 21 """ 22 return "{} {}".format(arg1, arg2)
创建userdefinedfilter.html文件,使用自定义filter模板代码:
1 {% load myfilter %} {# 加载自定义的filter模块 #} 2 <h4>单参数的自定义filter</h4> 3 {{ name_list_html.0|sb }} {# 使用自定义filter的sb方法 #} 4 <br/> 5 {{ name_list_html.1|sb }} {# 使用自定义filter的sb方法 #} 6 7 <hr> 8 9 <h4>多个参数的自定义filter</h4> 10 {{ name_list_html.0|self_str:"是好人吗?" }} {# 使用自定义filter的self_str方法,name_list_html.0是第一个参数,"是好人吗?"是第二个参数#} 11 <br/> 12 {{ name_list_html.0|self_str:"是好人" }} {# 使用自定义filter的self_str方法 #}
与模板对应的后台代码:
1 def defined_filter(request): 2 name_list = ["natasha", "john", "jane"] 3 4 return render(request, "userdefinedfilter.html", {"name_list_html": name_list})
1.4 tags
与逻辑相关的被称为tags。
1.4.1 for循环
for循环可用的一些参数:
Variable | Description |
---|---|
forloop.counter |
当前循环的索引值(从1开始) |
forloop.counter0 |
当前循环的索引值(从0开始) |
forloop.revcounter |
当前循环的倒序索引值(从1开始) |
forloop.revcounter0 |
当前循环的倒序索引值(从0开始) |
forloop.first |
当前循环是不是第一次循环(布尔值) |
forloop.last |
当前循环是不是最后一次循环(布尔值) |
forloop.parentloop |
本层循环的外层循环 |
for循环示例:
1 <h3>模版之循环操作</h3> 2 <ul> 3 <ol> 4 {% for name in namelisthtml %} 5 <li>{{ name }}</li> 6 {% endfor %} 7 </ol> 8 </ul> 9 10 <ul> 11 {% for name in namelisthtml %} 12 <li>{{ forloop.counter0 }} - {{ name }}</li> {# 循环迭代的次数,循环编号从0开始 #} 13 {% endfor %} 14 </ul> 15 16 <ul> 17 {% for name in namelisthtml %} 18 <li>{{ forloop.counter }} - {{ name }}</li> {# 循环迭代的次数,循环编号从0开始 #} 19 {% endfor %} 20 </ul> 21 22 <ul> 23 {% for name in namelisthtml %} 24 <li>{{ forloop.revcounter }} - {{ name }}</li> {# 循环迭代的次数,反着排列 #} 25 {% endfor %} 26 </ul> 27 28 <ul> 29 {% for name in namelisthtml %} 30 {% if forloop.first %} 31 <li class="c1">{{ forloop.counter}} - {{ name }}</li> 32 {% elif forloop.last %} 33 <li class="c2">{{ forloop.counter}} - {{ name }}</li> 34 {% else %} 35 <li>{{ forloop.counter}} - {{ name }}</li> 36 {% endif %} 37 {% endfor %} 38 </ul> 39 40 <ul> 41 {% for name in namelisthtml %} 42 {% if forloop.first %} 43 <li class="c1">{{ forloop.counter}} - {{ name }}</li> 44 {% else %} 45 <li class="{% if forloop.last %}c2{% endif %}">{{ forloop.counter}} - {{ name }}</li> {# for循环在class里也可以 #} 46 {% endif %} 47 {% endfor %} 48 </ul> 49 50 <hr> 51 <h3>模版之双层循环操作</h3> 52 <ul> 53 <ol type="A"> 54 {% for name_list in namelistshtml %} 55 <li>{{ forloop.counter }}</li> 56 {{ name_list }} 57 <ul> 58 <ol type="a"> 59 {% for name in name_list %} 60 <div> {{ forloop.parentloop.counter }} </div> {# 外层for循环 #} 61 <li>{{ forloop.counter }}</li> 62 {{ name }} 63 {% endfor %} 64 </ol> 65 </ul> 66 {% endfor %} 67 </ol> 68 </ul>
1.4.2 for empty
1 <h3>模版之for empty</h3> 2 {% for name in namelisthtml1 %} 3 {{ name }} {# 有数据就显示 #} 4 {% empty %} 5 {{ "没有数据" }} {# 没有数据就显示这里 #} 6 {% endfor %}
1.4.3 if elif else
if语句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判断。但不是支持连续判断!(类似a > b > c这种)。
1 {% if user_list %} 2 用户人数:{{ user_list|length }} 3 {% elif black_list %} 4 黑名单数:{{ black_list|length }} 5 {% else %} 6 没有用户 7 {% endif %}
注意,执行语句中的空格会严格出现在生成的HTML语句中!如下两段代码在生成的HTML语句中不一样:第二段代码的执行语句多了空格,因此生成的HTML语句会多出空格,如果要查找这些元素,而没有注意空格的问题,那么就会查找不到。
<a href="{% if menu.url_choice == 0 %}{% url menu.url_name %}{% else %} {{menu.url_name}} {% endif %}"> {{ menu.name }} </a>
<a href="{% if menu.url_choice == 0 %} {% url menu.url_name %} {% else %} {{menu.url_name}} {% endif %}"> {{ menu.name }} </a>
1.4.4 if else
1 {% if user_list|length > 5 %} 2 七座豪华SUV 3 {% else %} 4 黄包车 5 {% endif %}
1.4.5 with用法
with用来给变量取别名,一般用于变量名字过长时。
1 <h3>with用法</h3> 2 {% with name=namelistshtml.1.1 %} {# 变量名字太长时可以用with来取别名 #} 3 {{ name }} 4 {% endwith %}
1.5 自定义simple_tag
simple_tag和filter类似,区别在于simple_tag支持多参数(大于2)。
自定义simple_tag:在app文件夹下,创建templatetags包(名字固定),然后再新建mysimpletag.py文件作为自定义simple_tag。
1 """ 2 simple_tag和filter类似,区别在于simple_tag支持多参数(大于2) 3 """ 4 from django import template 5 6 7 register = template.Library() # 生成注册用的实例,固定写法 8 9 10 @register.simple_tag(name="mysimpletag") # 告诉Django的模版语言我现在有一个自定义的simple_tag方法,名字叫my_smp_tag。name参数这里仍然相当于取别名。 11 def my_smp_tag(arg1, arg2, arg3, arg4): 12 return "{} {} {} {}".format(arg1, arg2, arg3, arg4)
创建mysimpletag.html文件,使用自定义的simpletag:
1 {% load mysimpletag %} {# 加载自定义的simple_tag #} 2 {% mysimpletag "my" "name" "is" "druid" %}
与模版对应的后台代码示例:
1 def my_simple_tag(request): 2 3 return render(request, "mysimpletag.html")
1.6 inclusion_tag
inclusion_tag多用于返回html代码片段。
自定义inclusion_tag:在app文件夹下,创建templatetags包(名字固定),然后再新建inclusiontag.py文件作为自定义inclusion_tag。
1 """ 2 inclusion_tag用来返回html代码 3 """ 4 from django import template 5 6 7 register = template.Library() # 生成注册用的实例,固定写法 8 9 10 @register.inclusion_tag("results.html", name="myinclusiontag") 11 def my_inclusion_tag(n): 12 n = 1 if n < 1 else int(n) 13 data = ["第{}项".format(i) for i in range(1, n+1)] 14 15 return {"results": data} 16 17 18 """ 19 告诉Django的模版语言我现在有一个自定义的inclusion_tag方法,名字为myinclusiontag, 20 'results.html'文件是渲染HTML页面的内容 21 """
创建results.html文件
1 <ul> 2 {% for line in results %} 3 <li>{{ line }}</li> 4 {% endfor %} 5 </ul>
与模板对应的后台代码:
1 def my_inclusion_tag(request): 2 3 return render(request, "inclusiontag.html")
创建inclusiontag.html文件,使用inclusion_tag:
1 <body> 2 {% load inclusiontag %} 3 {% myinclusiontag 10 %} 4 5 <hr> 6 7 {% myinclusiontag 20 %} 8 </body>
1.7 注意事项
Django的模板语言中属性的优先级大于方法。以下举例说明:
1 def foo(request): 2 dict1 = {"a": 1, "b": 2, "c": 3, "items": "100"} 3 4 return render(request, "bar.html", {"data": dict1})
如上,我们在使用render方法渲染一个页面的时候,传的字典dict1有一个key是items并且还有默认的 dict1.items() 方法,此时在模板语言中:
{{ data.items }}
默认会取dicts的items key的值,为100。而不是返回items()方法本身返回的(键,值)元组。
2、母版
通过在母板中使用{% block blockName %} {% endblock %}
来定义"块",这个块可以被替换成不同的内容,一般用于页面会变化的地方。
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="x-ua-compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <title>Title</title> 8 {% block page-css %} {# 该块用来进行css替换 #} 9 10 {% endblock %} 11 </head> 12 <body> 13 14 <h1>下面部分是每个页面不同的部分,用来被替换</h1> 15 {% block page-content %} {# page-content代表要替换的块名 #} 16 17 {% endblock %} 18 <h1>母板底部</h1> 19 20 {% block page-js %} {# 该块用来进行js替换 #} 21 22 {% endblock %} 23 </body> 24 </html>
2.1 继承母版
在子页面中最上方使用下面的语法来继承母版。
{% extends "base.html" %}
然后再定义块的内容,与母版相对应的块就会被子页面中定义的块的内容所代替。
1 {% block page-content %} 2 html语法 3 ...... 4 {% endblock %}
3、组件
可以将常用的页面内容如导航条,页尾信息等组件保存在单独的HTML文件中,
1 <nav class="navbar navbar-inverse navbar-fixed-top"> 2 <div class="container-fluid"> 3 <div class="navbar-header"> 4 <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" 5 aria-expanded="false" aria-controls="navbar"> 6 <span class="sr-only">Toggle navigation</span> 7 <span class="icon-bar"></span> 8 <span class="icon-bar"></span> 9 <span class="icon-bar"></span> 10 </button> 11 <a class="navbar-brand" href="https://v3.bootcss.com/examples/dashboard/#">BMS</a> 12 </div> 13 </div> 14 </nav>
然后在需要使用的地方按如下语法导入即可。
{% include 'navbar.html' %}
4、静态文件
把路径写死的导入静态文件方法:
1 <link rel="stylesheet" type="text/css" href="/static/bootstrap/css/bootstrap.css"/> 2 <link rel="stylesheet" type="text/css" href="/static/dashboard.css"/> 3 <link rel="stylesheet" type="text/css" href="/static/fontawesome/css/font-awesome.css"/> 4 5 <script type="text/javascript" src="/static/jquery-3.3.1.js"></script> 6 <script type="text/javascript" src="/static/bootstrap/js/bootstrap.js"></script>
用{% load static %}的导入静态文件方法:
1 {% load static %} {# 会去settings.py里面查找静态文件夹的别名 #} 2 <link rel="stylesheet" type="text/css" href="{% static 'bootstrap/css/bootstrap.css' %}"/> {# 将别名和相对路径进行拼接 #} 3 <link rel="stylesheet" type="text/css" href="{% static 'dashboard.css' %}"/> 4 <link rel="stylesheet" type="text/css" href="{% static 'fontawesome/css/font-awesome.css' %}"/> 5 6 <script type="text/javascript" src="{% static 'jquery-3.3.1.js' %}"></script> 7 <script type="text/javascript" src="{% static 'bootstrap/js/bootstrap.js' %}"></script>
还可以用get_static_prefix方法:
{% load static %}
<script type="text/javascript" src="{% get_static_prefix %} bootstrap/js/bootstrap.js"></script>
或者:
{% load static %} {% get_static_prefix as STATIC_PREFIX %} <script type="text/javascript" src="{{ STATIC_PREFIX }} bootstrap/js/bootstrap.js"></script>
某个文件被多处引用时,还可以将其存为一个变量。
1 {% load static %} 2 {% static "images/hi.jpg" as myphoto %} 3 <img src="{{ myphoto }}"></img>