Django 模板层
Django模板层
模板层的主要任务就是如何将页面呈现给用户,用户能看到的所有东西都是属于模板层进行呈现的。
HTML
文件就是模板,模板是会先经过渲染,再呈现给用户。
学习模板层主要有以下两点:
变量相关或处理均使用
{{}}
,即模板变量。逻辑相关或处理均使用
{%%}
,即模板标签。
模板变量传值
其实在聊Django
视图层的时候介绍的render()
方法中就允许将视图函数中的变量传递给模板。
模板中使用{{name}}
即可完成调用。
def test(request):
username = "云崖先生"
return render(request,"test.html",locals())
<body>
{{username}}
</body>
传值类型
Python
中所有基本数据类型均可以进行模板传递,但是函数、类等可调用类型会出现一些令人意外的情况。
注意:可调用类型传递时会自动加括号调用,并将返回值进行渲染(如果没有返回值则为
None
),但是不支持参数的传递。
后端返回:
def test(request):
STR = "云崖先生"
INT = 100
FLOAT = 11.11
BOOL = True
DICT = {"v1":"k1"}
SET = {"1","2","3"}
TUPLE = (1,2,3)
LIST = [1,2,3,4]
def func():
return "function"
class T1(object):
pass
return render(request,"test.html",locals())
模板接收:
<body>
<p>{{STR}}</p>
<p>{{INT}}</p>
<p>{{FLOAT}}</p>
<p>{{BOOL}}</p>
<p>{{DICT}}</p>
<p>{{SET}}</p>
<p>{{TUPLE}}</p>
<p>{{LIST}}</p>
<p>{{func}}</p> <!-- 不支持参数传递 -->
<p>{{T1}}</p> <!-- 实例化出了一个类 -->
</body>
渲染结果:
云崖先生
100
11.11
True
{'v1': 'k1'}
{'1', '2', '3'}
(1, 2, 3)
[1, 2, 3, 4]
function
<app01.views.test.<locals>.T1 object at 0x000001C1E00ACB38>
元素取出
无论后端的变量是list
或者dict
,均可使用.
语法进行取值。
传值:
def test(request):
DICT = {"hobby":["basketball","football","volleyball"]}
return render(request,"test.html",locals())
取值:
{{DICT.hobby.2}}
结果:
volleyball
模板变量过滤
由于后端的函数模板中使用函数无法传递参数,所以提供自带的过滤器进行操作。
过滤器有非常多的功能,大概有六十多种过滤器提供使用,这里只介绍常用的。
使用示例
以下举例常用的模板过滤器
过滤器 | 描述 |
---|---|
{{v1|length}} | 获取v1的长度 |
{{v1|default:v2}} | 如果v1为真,则使用v1,否则使用v2 |
{{v1|filesizeformat}} | v1必须是int类型,对其进行KB、MB、GB、TB 等进制之间的转换 |
{{v1|date:"Y-m-d X"}} | v1必须是时间类型,对其进行自定义格式化 |
{{v1|slice:"start:stop:step"}} | v1必须是可通过索引取值的类型,对其进行切片 |
{{v1|truncatechars:num}} | v1必须是可通过索引取值的类型,对其截取指定长度的字符(包含三个. ) |
{{v1|truncatewords:num}} | v1必须是字符串类型,对其进行截取特点数量的单词,以空格分隔(不包含. ) |
{{v1|cut:'v2'}} | 移除v1中特定的字符v2 |
{{v1|join:'v2'}} | 使用v2对v1进行拼接 |
{{v1|add:v2}} | v1与v2进行相加,如都是int则为加法,如都是str则为拼接 |
{{v1|safe}} | 识别v1中的HTML代码并进行渲染(默认不会进行渲染) |
后端返回:
def test(request):
import datetime
v_1 = "12345"
v_2 = False
v_3 = 10240
v_4 = datetime.datetime.now()
v_5 = ["一","二","三","四","五","六","七"]
v_6 = "abcdefghijklmn"
v_7 = "HELLO WORD HELLO DJANGO"
v_8 = "ABC$$$"
v_9 = "QWETYU"
v_10 = 10
v_11 = "<h1>标题一<h1>"
return render(request,"test.html",locals())
模板接收加过滤:
<body>
<p>{{v_1|length}}</p>
<p>{{v_2|default:1}}</p>
<p>{{v_3|filesizeformat}}</p>
<p>{{v_4|date:"Y-m-d h:m:s"}}</p>
<p>{{v_5|slice:"1:4:2"}}</p>
<p>{{v_6|truncatechars:6}}</p>
<!-- 上面的: 三个点占三个位置 -->
<p>{{v_7|truncatewords:3}}</p>
<!-- 上面的:三个点不占位置 -->
<p>{{v_8|cut:"$"}}</p>
<p>{{v_9|join:"---"}}</p>
<p>{{v_10|add:10}}</p>
{{v_11|safe}}
</body>
渲染结果:
后端渲染
默认的,后端如果返回HTML
代码模板层是不会进行渲染的,这是为了防止XSS
攻击。
但是如果使用mark_safe()
进行包裹,则会进行渲染。
def test(request):
from django.utils.safestring import mark_safe # 导入模块
res = "<h1>不会渲染<h1>"
res_safe = mark_safe("<h1>会渲染<h1>")
return render(request,"test.html",locals())
<body>
{{res}}
{{res_safe}}
</body>
模板标签分支
基本使用
模板中支持对{% if %}、{% else %}、{% elif %}
进行使用,与Python
中逻辑一致。
注意:判断完成后要使用
{% endif %}
进行结束判断的标志
<body>
{% if user %} <!-- 在{%%}的模板标签中,被{{}}套住的变量可以不用写{{}}-->
<p>有内容</p>
{% else %}
<p>无内容</p>
{% endif %}
</body>
模板标签循环
基本使用
模板中支持{% for %}
来进行操作。
注意:循环完成后要使用
{% endfor %}
进行结束循环的标志
li = ["A","B","C"]
<body>
{% for row in li %}
<p> {{row}} </p> <!-- 在{%%}外使用变量,依然要嵌套上{{}} -->
{% endfor %}
</body>
A
B
C
forloop
{{forloop}}
记录了一些循环的信息。
如下所示:
li = ["A","B","C"]
<body>
{% for row in li %}
<p> {{forloop}} </p> <!-- 可使用 {{forloop.counter/first/last}} 等 -->
{% endfor %}
</body>
不可循环
如果一个迭代对象为空,则不可循环。此时可以使用{% empty %}
进行操作
li = []
<body>
{% for row in li %}
<p>不为空</p>
{% empty %}
<p>迭代对象为空</p> <!--执行这里-->
{% endfor %}
</body>
键值方法
在循环中,可以对被遍历的对象使用keys、values、items
进行操作。
这与Python
中是一样的。
dic = {"name":"云崖","age":"18","gender":"男",}
<body>
{% for key in dic.keys %}
<p>{{key}}</p>
{% endfor %}
{% for value in dic.values %}
<p>{{value}}</p>
{% endfor %}
{% for kv in dic.items %}
<p>{{kv}}</p>
{% endfor %}
</body>
----------------
name
age
gender
----------------
云崖
18
男
----------------
('name', '云崖')
('age', '18')
('gender', '男')
模板标签别名
可以使用{% with %}
配合as
对嵌套复杂的单项数据做一个别名。方便后续进行拿出,更推荐与循环进行配套使用。
li = [
{"username":"Yunya"},
{"username":"Jack"},
{"username":"Tom"},
]
----------------
<body>
{% for row in li %}
{% with row.username as data %}
<p>{{data}}</p>
<!-- 如果不使用别名,则需要写 {{row.username}} 这只是一个示例,实际的嵌套可能比这个复杂的多 -->
{% endwith %}
{% endfor %}
</body>
----------------
Yunya
Jack
Tom
自定义系列
所有的,自定义都需要完成以下三步骤:
1.在
APP
中创建templatetags
文件夹,必须叫这个名字2.在该文件夹下创建一个
py
文件,可以是任意名字3.导入一个模块,并写一句话
from django import template
register = template.Library()
自定义过滤器
先完成上面三步,再进行过滤器的创建。
自定义过滤器的作用就是丰富内置的过滤器,完成你的需求。
注意:过滤器的调用是
{{ v1|filter:v2 }}
,过滤器最多只能传递两个参数,且第一个参数在|
左边,第二个参数传递的格式必须是过滤器名字:
右边的参数。
自定义过滤器全步骤:
# app01/templatetags/tags.py
from django import template
register = template.Library()
@register.filter(name="f1") # 在使用时,使用的是这个名字。函数名可以随便取。注意一定要关键字传参
def f1(left, right): # left 代表斜杠左边的值,right代表斜杠右边的值
return left + right
基本使用:
<body>
{% load tags %} <!-- 先导入标签的文件,tags.py -->
{{1|f1:2}} <!-- 最多两个参数,参数1|过滤器名字:参数2 这是固定格式
</body>
-------
3
自定义标签
自定义标签与自定义过滤器比较相似但是也有两大不同。
1.支持无限制的参数传递
2.参数传递方式不同
注意:标签的调用是
{% tag v1 v2 v3 %}
,这与过滤器有较大的不同。
先完成上面三步,再进行标签的创建。
# app01/templatetags/tags.py
from django import template
register = template.Library()
@register.simple_tag(name="t1") # 在使用时,使用的是这个名字。函数名可以随便取。注意一定要关键字传参
def t1(v1, v2, v3):
return v1 + v2 + v3
基本使用:
<body>
{% load tags %} <!-- 先导入标签的文件,tags.py -->
{% t1 1 2 3%} <!--参数无限制-->
</body>
自定义inclusion_tag
自定义inclusion_tag
与上面两个都不太一样,它返回的是一个局部页面,当你的项目中有很多网页中都有相同的一部分网页结构,则使用自定义inclusion_tag
来完成。
还是需要先完成上面三个步骤。
# app01/templatetags/tags.py
from django import template
register = template.Library()
@register.inclusion_tag("common.html") # 去根目录找templates文件夹下的common.html文件
def common_html(username): # 注意,这里调用是函数名,而不是上面指定的名字了
user = username
return locals() # 返回当前命名空间中所有变量名
# 项目根目录/templats/common.html
<h1>当前登录:{{user}}</h1> # 可以拿到 common_html 函数中的所有变量名了
<h1>点击注销</h1>
# 被渲染的模板
<body>
{% load tags %}
{% common_html "云崖" %} <!---这一处将会替换为指定的页面,即common.html里的内容-->
</body>
模板继承引入
模板继承
模板继承是全栈开发里面Django
所提供的非常牛逼的一个功能,它允许你一套HTML
模板能被其他模板进行继承并修改一些特定区域的内容。
# 项目根目录/templats/common.html
{% block left %}
<!-- 这里面可以允许修改,相当于给这块区域起一个名字,left -->
{% endblock %}
============================
# 项目根目录/templats/other.html
{% extends 'common.html' %}
<!-- 继承common.html -->
{% block left %}
<!-- 执行修改 -->
{% endblock %}
接下来看一下演示代码就知道了。
这里放一套公用的左右布局页面
# 项目根目录/templats/common.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src='https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js'></script>
<link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css'
integrity='sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u' crossorigin='anonymous'>
<script src='https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js'
integrity='sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa'
crossorigin='anonymous'></script>
<title>Document</title>
{% block CSS %}
{% endblock %}
</head>
<body>
<header class="navbar navbar-inverse">
<!-- 头部,不允许改变 -->
<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="#">Project name</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="#">Home</a></li>
<li><a href="#about">About</a></li>
<li><a href="#contact">Contact</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">Dropdown <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li class="dropdown-header">Nav header</li>
<li><a href="#">Separated link</a></li>
<li><a href="#">One more separated link</a></li>
</ul>
</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="../navbar/">Default</a></li>
<li><a href="../navbar-static-top/">Static top</a></li>
<li class="active"><a href="./">Fixed top <span class="sr-only">(current)</span></a></li>
</ul>
</div>
</header>
<main class="container-full">
<div class="row">
<section class="col-sm-3 col-md-2 sidebar">
<!-- 左边,允许改变一小块区域 -->
<ul class="nav nav-sidebar">
<li class="active"><a href="#">menu1 <span class="sr-only">(current)</span></a></li>
<li><a href="#">menu2</a></li>
<li><a href="#">menu3</a></li>
<li><a href="#">menu4</a></li>
</ul>
{% block left %}
<!-- 这里面可以允许修改,相当于给这块区域起一个名字,left -->
{% endblock %}
</section>
<section class="col-lg-6">
{% block right %}
<!-- 这里面可以允许修改,相当于给这块区域起一个名字right -->
{% endblock %}
</section>
</div>
</main>
</body>
{% block Js %}
{% endblock %}
</html>
# 项目根目录/templats/其他页面.html
{% extends 'common.html' %}
<!-- 继承common.html -->
{% block left %}
<!-- 修改内容 -->
<ul class="nav nav-sidebar">
<li><a href="">menu-div-1</a></li>
<li><a href="">menu-div-2</a></li>
<li><a href="">menu-div-3</a></li>
</ul>
{% endblock %}
{% block right %}
<!-- 修改内容 -->
<h4>Subheading</h4>
<p>Donec id elit non mi porta gravida at eget metus. Maecenas faucibus mollis interdum.</p>
<h4>Subheading</h4>
<p>Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Cras mattis consectetur purus sit amet fermentum.</p>
<h4>Subheading</h4>
<p>Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
{% endblock %}
{% block css %}
<!-- 修改CSS -->
{% endblock %}
{% block js %}
<!-- 修改Js -->
{% endblock %}
模板引入
模板引入是在一个HTML
中引入一段公用的HTML
代码。
# 项目根目录/templats/common.html
<h1>公用咯<h1>
============================
# 项目根目录/templats/other.html
{include "common.html"} <!-- 将common.html中代码全部拿过来 -->