一. 前言
Django模板层的知识包括标签、过滤器、自定义标签、自定义过滤器以及inclusion_tag,最重要的是模板的继承和导入。
首先模板层最重要的是模板语法,之前我们提过涉及到变量用模板语法{{ }},涉及到逻辑用模板语法{% %},这里再追加几点。过滤器在模板语法{{ }}中写,而且只能传两个参数;标签在模板语法{%%}中写,而且能传多个参数(参数间用空格隔开)。模板的继承与导入也是在模板语法{%%}中写。
接下来再回顾一下后端朝前端页面传递数据的两种方式:
# 第一种
return render(request, 'index.html', {'nums': nums}) # 第二种
return render(request, 'index.html', locals())
该两种方法有利有弊,不过我们偏爱locals()。
后端除了能向前端提交python的基本数据类型:int、float、str、list、dict、tuple、set数据外,还能提交函数与类的对象。当我们传递函数名给前端时,会自动调用该函数拿到函数return的结果,但是不支持传参,所以只能传递无参函数名。当传递类的对象obj给前端时,相当于print(obj),前端拿到的是该obj的内存地址,因为print(obj)走的是类的__str__()方法。所以传递类的对象时,我们可以重写__str__()让前端拿到想要的内容。
前端想要获取后端传递过来的容器类型中的元素时,统一使用句点符(.):
<!--假设前端拿到的数据是 L = [1, 2, 3] D = {'age': 18}-->
{{ L }} <!--[1, 2, 3]-->
{{ L.1 }} <!-- 2 索引也是从0开始--> {{ D }} <!--{'age': 18}-->
{{ L.age }} <!-- 18 -->
前端可以调用python自带的内置方法,不过只能调用不需要传参的,比如字典dic的dic.keys(),dic.values(),dic.items()等,注意:在前端调用方法不需要加(),直接dic.keys即可。
模板语法的注释不会展示到前端(用谷歌浏览器的检查也看不到),只能在后端看到,原生的HTML注释前后端都可以看到。
二. 过滤器
过滤器在模板语法{{ }}中书写,实际上过滤器内部走的是后端的语法,过滤器最多支持传两个参数(会把|左边的参数当做过滤器的第一个参数传进去),不过可以把参数改成容器类型,然后在内部动手脚处理参数中的数据,间接实现传递多参数,这需要我们自定义过滤器来实现。
过滤器基本语法:{{ 参数1|过滤器名字:参数2 }} 有些过滤器没有参数2。
- 统计字符串长度 {{ s|length }}:会将s当做length的参数传进去,内部就是后端语法
- 获取的数据为空就返回default后面定义的值{{ s|default:'s是空的字符串' }}
- 将数字转化为成表示文件大小的格式{{ file_size|filesizeformat }}
如果file_size是204800,执行该过滤器后是 200.0KB
- 格式化时间{{ ctime|date:'Y-m-d' }}
不需要同后端一样加%('%Y-%m-%d'),如果ctime是2019-06-11 16:56:38.903314,执行完该过滤器后是2019-06-11。
- 字符串的切片操作{{ s|slice:'0:8:2' }}
如果s='hello world',执行完该过滤器后是‘hlow’,slice后面的参数同python字符串切片参数一样,开头:结尾:步长,其中步长默认为1,切片顾头不顾尾。
- 截取固定长度的字符串{{ s|truncatechars:10 }}
如果s='hello world',执行完该过滤器后是‘hello w...’,超出长度的部分会用...代替,当我们参数为10时,实际上截取的字符串长度为7,因为...占3位。
- 按照空格截取文本内容{{ s|truncatewords:2 }}
如果s='he llo wor ld',执行完该过滤器后是:‘he llo ...’,截取至前两个空格,之后的内容用...表示。
- 加法亦或是字符串拼接{{ n1|add:n2 }}
如果n1=2, n2=4,那么结果为6。如果n1='hello', n2='world',那么结果为'helloworld'。
重点:
当我们后端向前端提交的字符串含有标签时,比如'<h1>this is title</h1>',为了防止脚本攻击(比如<script>while(1){alert('come on')}</script>),前端之后把你当成普通的字符串。当我们想要让前端帮我们执行提交的字符串中的标签内容时,有两种方式。
- 在前端通过{{ 字符串|safe }}告诉前端字符串很安全,让它大胆执行。
- 后端用模块操作一下字符串,告诉前端我们给它的字符串很安全。
from django.utils.safestring import mark_safe def index(request):
html = reverse('index')
s = '<h1>this is title</h1>'
s = mark_safe(s)
return render(request, 'index.html', locals()) # 前端直接{{ s }}即可
二. 标签
标签在模板语法{%%}中书写,{%%}涉及到逻辑,标签一般是for循环,if判断以及它们的嵌套使用,除此之外还有empty标签。
for循环中可以使用forloop来给我们提供一些信息:
1. for循环
<!--l = [1, 2, 3, 4, 5]-->
{% for foo in l %}
<p>{{ foo }}</p>
<p>{{ forloop }}</p>
{% endfor %}
2. if判断
{% if flag %}
<p>flag不为空</p>
{% else %}
<p>flag是空</p>
{% endif %}
3. if与for嵌套使用
{% for foo in s %}
{% if forloop.first %}
<p>这是第一次</p>
{% elif forloop.last %}
<p>这是最后一次</p>
{% else %}
<p>keep up</p>
{% endif %}
{% endfor %}
4. empty
{% for foo in l %}
{% if forloop.first %}
<p>这是我的第一次</p>
{% elif forloop.last %}
<p>这是最后一次了啊</p>
{% else %}
<p>嗨起来!!!</p>
{% endif %}
{% empty %}
<p>你给我的容器类型是个空啊,没法for循环</p>
{% endfor %}
5. 补充
当后端传来的数据中,我们想要的数据需要句点符点很多次才能拿到且后面还需要用到该数据时,我们可以选择给该数据取别名。
三.自定义过滤器与标签、inclusion_tag
要自定义过滤器与标签,必须先做三件事:
- 在应用文件夹下新建templatetags文件夹(必须叫这个名字)
- 在templatetags文件夹中新建一个py文件,名字任意,不过最好不要有中文
- 在该py文件中固定写以下两句代码:
from django import template register = template.Library()
再次强调,一定要按照以上步骤进行,文件夹一定要叫templatetags!!!
1. 自定义过滤器
from django import template register = template.Library() # 自定义过滤器
@register.filter(name='my_add') # name是HTML页面中调用该过滤器用的名字
def my_add(a, b):
return a+b
2. 自定义标签
from django import template register = template.Library() @register.simple_tag(name='my_tag')
def my_tag(a, b, c):
return a + b + c
3.自定义inclusion_tag
from django import template register = template.Library() @register.inclusion_tag('login.html', name='login')
def login(n):
l = ['第%s项' % i for i in range(n)]
return {'l': l}
<!--login.html-->
<ur>
{% for foo in l %}
<li>{{ foo }}</li>
{% endfor %}
</ur>
在该例子中,我们在前端调用inclusion_tag时:
{% load my_tags %}
{% login 5 %}
首先会将参数5传给login函数,函数执行完后的结果提交至login.html,然后将login.html的页面显示在调用该inclusion_tag的地方。
inclusion_tag会将渲染过的login.html界面返回至调用它的位置,正是因为这种机制,所以它所渲染那个html页面中可以不需要head和body等各种标签,你需要返回什么就在里面写什么。
4.结论
想要使用自定义的过滤器、标签和inclusion_tag,必须要先在html界面中加载该模块
<!--加载my_tags.py模块-->
{% load my_tags %} {{ n1|my_add:n2 }} <!--调用自定义过滤器-->
{% my_tag n1 n2 n3 %} <!--调用自定义标签-->
{% login 5 %} <!--调用自定义inclusion_tag-->
四. 模板的继承与导入
1. 模板的继承
1.1 首先需要被继承的模板中划分多个区域:
{% block 给区域起的名字 %} {% endblock %}
1.2 通常情况下一个模板中至少有三块区域:
{% block css %}
页面css代码块
{% endblock %} {% block js %}
页面js代码块
{% endblock %} {% block content %}
页面主体内容
{% endblock %}
1.3 子模板继承模板,首先要先继承模板所有的内容:
{% extends 'home.html' %}
1.4 根据被block快的名字修改指定区域的内容
{% block content %}
<h1>登录页面</h1>
<form action="">
<p>username:<input type="text" class="form-control"></p>
<p>password:<input type="text" class="form-control"></p>
<input type="submit" class="btn btn-success">
</form>
{% endblock %}
1.5 可以使用{{ block.super }}使用父模板的该block的内容(使用该方法是没有提示,要手打)
{% block content %}
{{ block.super }}
{% endblock %}
注意:原模板中的block区域越多越好,因为越多代表可以修改的细节很多,扩展性就越强。继承后,子模板只能修改block区域中的内容。
2. 模板的导入
模板导入同自定义标签inclusion_tag有点像,都是将一个HTML页面显示到调用它们的地方,而且他们的HTML页面都是要啥就写啥就行了,不需要body、head等标签。不过inclusion_tag是动态的,可以传递参数,而模板导入是静态的。
模块导入{% include '想导入的html文件名' %}:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login2</title>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
<link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<!--导入模板index2.html-->
{% include 'index2.html' %}
</body>
</html>
<h1>我是这条gai最靓的仔</h1>
<form action="">
<p><input type="text"></p>
<p><input type="text"></p>
<input type="submit">
</form>
index2.html
3. 静态文件配置
当我们为了让路由名字改变时我们不需要修改HTML页面和视图函数中的内容,我们引入了反向解析。同理,为了防止settins.py文件夹中static文件夹的接口前缀改变而我们不需要html页面中head中script和link的接口前缀(需要修改的前提是他们导入的是static文件夹中的东西),我们引入了静态文件配置。
{% load static %} <link rel='stylesheet' href="{% static 'css/mycss.css'%}"> # 第一种方式
<link rel='stylesheet' href="{% get_static_prefix %}css/mycss.css"> # 第二种方式