一 引子

什么是模版系统?这里做一个简单解释。要想明白什么是模版系统,那么我们得先分清楚静态页面和动态页面。我们之前学过的都是静态页面,所谓的静态页面就是浏览器向后端发送一个请求,后端接收到这个请求,然后返回给浏览器一个html页面,这个过程不涉及从数据库取出数据渲染到html页面上,只是单纯的返回一个页面(数据全部在html页面上)。而动态页面就是在给浏览器返回html页面之前,需要后端与数据库之间进行数据交互,然后将数据渲染到html页面上在返回给浏览器。言外之意静态页面不涉及数据库,动态页面需要涉及从数据库取出数据。那么模版系统是什么呢?如果你只是单纯的写静态页面,也就没必有必要用模版系统了,只用动态页面才需要模版系统。

简单来说,模版系统就是在html页面想要展示的数据库或者后端的数据的标签上做上特殊的占位(类似于格式化输出),通过render方法将这些占位的标签里面的数据替换成你想替换的数据,然后再将替换数据之后的html页面返回给浏览器,这个就是模版系统。

模板渲染的官方文档

关于模板渲染你只需要记两种特殊符号(语法):

{{  }}和 {% %}

变量相关的用{{}},逻辑相关的用{%%}。

 

二、变量

1. 简单示例

接下来,我们先搭一个简单流程:浏览器访问https://127.0.0.1:8000:/index,返回一个index.html页面,我们在views函数中设置一些变量,然后通过模版系统渲染,最终返回给浏览器。

url:

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^index/',views.index),
]

views:

render第三个参数接受一个字典的形式,通过字典的键值对index.html页面进行渲染,这也就是模块渲染。

def index(request):
    name = '太白金星'
    age = 18
    name_list = ['王阔', '天琪', '傻强', '志晨', '健身哥']
    dic = {'classname': '人工智能', 'since': 2019}

    return render(request, 'index.html',{'name': name, 'age': age,'name_list':name_list, 'dic_class':dic})

html:

<!DOCTYPE html>
<html lang="zh-CN">
<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>Bootstrap 101 Template</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet">


</head>
<body>
<h1>你好,世界!</h1>

<ul>
    {#  通过render进行模版语言渲染然后替换成后端的数据,最终发给浏览器  #}
    <li>{{ name }}</li>
    <li>{{ age }}</li>
    <li>{{ name_list }}</li>
    <li>{{ dic_class }}</li>
</ul>
</body>
</html>

最终浏览器显示的结果为:

Django基础之:模版系统

 

这样,你后端这些变量全部都渲染到前端了。

2. 语法

在Django的模板语言中按此语法使用:{{ 变量名 }}。

  当模版引擎遇到一个变量,它将计算这个变量,然后用结果替换掉它本身。 变量的命名包括任何字母数字以及下划线 ("_")的组合。 变量名称中不能有空格或标点符号。

  深度查询据点符(.)在模板语言中有特殊的含义。当模版系统遇到点("."),它将以这样的顺序查询:

    字典查询(Dictionary lookup)
    属性或方法查询(Attribute or method lookup)
    数字索引查询(Numeric index lookup)

3. 万能的点 . 

通过简单示例我们已经知道模版系统对于变量的渲染是如何做到的,非常简单,接下来我们研究一些深入的渲染,我们不想将整个列表或者字典渲染到html,而是将列表里面的元素、或者字典的某个值渲染到html页面中,那怎么做呢?就是通过万能的点。

views:

def index(request):
  
    name_list = ['王阔', '天琪', '傻强', '志晨', '健身哥']
    dic = {'classname': '人工智能', 'since': 2019}
    lis = [1, ['冠状病毒', '武汉加油'],3]

    # 使用locals是用于测试,实际生产环境中是不可以的。
    return render(request, 'index.html', locals())

html:

<!DOCTYPE html>
<html lang="zh-CN">
<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>Bootstrap 101 Template</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet">


</head>
<body>
<h1>你好,世界!</h1>

<ul>

    {# 万能的点 #}
    <li>{{ name_list.2 }}</li>
    <li>{{ dic.classname }}</li>
    <li>{{ lis.1.0 }}</li>
</ul>
</body>
</html>

浏览器的显示结果:

Django基础之:模版系统

刚才我们尝试的数据类型,那么在一切皆对象的python世界中,我们一个对象是否可以通过模版渲染到html页面中呢?

views:

def index(request):
  
    class A:

        def __init__(self):
            self.name = 'barry'

        def func(self):
            return '太白教你学python'

    obj = A()
    return render(request, 'index.html', locals())

html:

<!DOCTYPE html>
<html lang="zh-CN">
<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>Bootstrap 101 Template</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet">


</head>
<body>
<h1>你好,世界!</h1>

<ul>
   
    {# 万能的点 #}

    <li>{{ obj.name }}</li>
    <li>{{ obj.func }}</li>
</ul>
</body>
</html>

浏览器显示的结果:

Django基础之:模版系统

 

如图所示,对象也是可以通过模版渲染到html页面中的,但是这里要注意一个点:obj.func是不可以加括号的,所以我们后端设置的函数是不可以有参数的(类中的方法self自动传递)。

注意我们直接在js代码中使用模板语法的时候,模板渲染的时候会有个转义的动作,将s = ['哈哈','xx']这种数据中的元素的引号变为一个特殊符号:这个我们后面会讲到

    <script>
        // 不加safe的话,引号会被转义。
// var a = {{ s }} // var a = [&#39;哈哈&#39;, &#39;xx&#39;]; // console.log(a[0]) // 加上safe就正常了 var a = {{ s|safe }}; console.log(a[0])
// 还要注意,当我们模板渲染的时候,后端返回的数据是字符串的话,我们需要将{{ s }}外面加上引号
比如s = '哈哈'
js中的写法
var a = '{{ s }}' </script>

三、过滤器

1.什么是过滤器

有的时候我们通过render渲染到html的数据并不是我们最终想要的数据,比如后端向前端传递的数据为hello,但是我们html想要显示为HELLO,当然这个在后端是可以提前处理的,但是诸如此类的需求我们可以通过过滤器解决,过滤器给我们提过了很多便捷的方法,加工你传递到html的数据,便于灵活开发。

2.语法

  过滤器的语法: {{ value| filter_name:参数 }}

  使用管道符"|"来应用过滤器。

  例如:{{ name| lower }}会将name变量应用lower过滤器之后再显示它的值。lower在这里的作用是将文本全都变成小写。

  注意事项:

  1. 过滤器支持“链式”操作。即一个过滤器的输出作为另一个过滤器的输入。
  2. 过滤器可以接受参数,例如:{{ sss|truncatewords:30 }},这将显示sss的前30个词。
  3. 过滤器参数包含空格的话,必须用引号包裹起来。比如使用逗号和空格去连接一个列表中的元素,如:{{ list|join:', ' }}
  4. '|'左右没有空格!没有空格!没有空格!

  Django的模板语言中提供了大约六十个内置过滤器。

3.常用过滤器

default: 如果一个变量是false或者为空,使用给定的默认值。 否则,使用变量的值。

views:
a = ''  # 没有变量a或者变量a为空

html: # 显示啥也没有 {{ value|default:"啥也没有"}}

length:返回值的长度,作用于字符串和列表。

views:
name_list = ['王阔', '天琪', '傻强', '志晨', '健身哥']
s = '太白金星讲师'
html:
{{ name_list|length }}  # 显示为5
{{s|length }}  # 显示为6

filesizeformat: 将值格式化为一个 “人类可读的” 文件尺寸 (例如 '13 KB''4.1 MB''102 bytes', 等等)。

views:
value = 1048576

html:
{{ value|filesizeformat }}  # 显示为1.0MB

切片,支持pyhton中可以用切片的所有数据类型

views:
name_list = ['王阔', '天琪', '傻强', '志晨', '健身哥']
s = '太白金星讲师'

html:
{{ name_list|slice:'1:3' }}  # ['天琪', '傻强']
{{ s|slice:'1::2' }}  # '白星师'

date:时间格式化

views:
time = datetime.datetime.now()

html:
{{ t|date:"Y-m-d H:i:s" }}  # 2020-02-11 07:31:29

 关于时间日期的可用的参数(除了Y,m,d等等)还有很多,有兴趣的可以去查查看看。

truncatechars:如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾。

参数:截断的字符数

views:
describe = '1999年3月,马云正式辞去公职,后来被称为18罗汉的马云团队回到杭州,凑够50万元人民币'

html:
{{ describe|truncatechars:9 }}  # 1999年3...

# 截断9个字符,三个点也算三个字符

这个我们在浏览网页时经常见到,描述的内容很多,只能用...显示,比如:

Django基础之:模版系统

 

 

 

truncatewords:在一定数量的字后截断字符串,是截多少个单词。

views:
words = 'i love you my country china'

html:
{{ words|truncatewords:3 }}  # i love you...

cut: 移除value中所有的与给出的变量相同的字符串

views:
words = 'i love you my country china'

html:
{{ words|cut:3 }}  # iloveyou

join: 设定连接符将可迭代对象的元素连接在一起与字符串的join方法相同。

views:
name_list = ['王阔', '天琪', '傻强', '志晨', '健身哥']
dic = {'name':'太白','age': 18}
tu = (1, 2, 3)

html:
<li>{{ name_list|join:'_'}}</li>
<li>{{ dic|join:','}}</li>
<li>{{ tu|join:'+'}}</li>
'''
王阔_天琪_傻强_志晨_健身哥
name,age
1+2+3
'''

safe

  Django的模板中在进行模板渲染的时候会对HTML标签和JS等语法标签进行自动转义,原因显而易见,这样是为了安全,django担心这是用户添加的数据,比如如果有人给你评论的时候写了一段js代码,这个评论一提交,js代码就执行啦,这样你是不是可以搞一些坏事儿了,写个弹窗的死循环,那浏览器还能用吗,是不是会一直弹窗啊,这叫做xss攻击,所以浏览器不让你这么搞,给你转义了。但是有的时候我们可能不希望这些HTML元素被转义,比如我们做一个内容管理系统,后台添加的文章中是经过修饰的,这些修饰可能是通过一个类似于FCKeditor编辑加注了HTML修饰符的文本,如果自动转义的话显示的就是保护HTML标签的源文件。为了在Django中关闭HTML的自动转义有两种方式,如果是一个单独的变量我们可以通过过滤器“|safe”的方式告诉Django这段代码是安全的不必转义。

  我们去network那个地方看看,浏览器看到的都是渲染之后的结果,通过network的response的那个部分可以看到,这个a标签全部是特殊符号包裹起来的,并不是一个标签,这都是django搞得事情。

    Django基础之:模版系统

  比如:value = "<a href='#'>点我</a>"   和   value="<script>alert('123')</script>"

{{ value|safe}}

    很多网站,都会对你提交的内容进行过滤,一些敏感词汇、特殊字符、标签、黄赌毒词汇等等,你一提交内容,人家就会检测你提交的内容,如果包含这些词汇,就不让你提交,其实这也是解决xss攻击的根本途径,例如博客园:

    Django基础之:模版系统  

timesince(了解)

将日期格式设为自该日期起的时间(例如,“4天,6小时”)。

采用一个可选参数,它是一个包含用作比较点的日期的变量(不带参数,比较点为现在)。 例如,如果since_12是表示2012年6月28日日期实例,并且comment_date是2018年3月1日日期实例:

views:
  year_12 = datetime.datetime.now().replace(year=2012, month=6, day=28)
  year_18 = datetime.datetime.now().replace(year=2018, month=3, day=1)

html:
 <li>{{ year_12|timesince:year_18}}</li>

'''
5 years, 8 months
'''

如果year_18不写,默认就是距现在的时间段。

timeuntil(了解)

  似于timesince,除了它测量从现在开始直到给定日期或日期时间的时间。 例如,如果今天是2006年6月1日,而conference_date是保留2006年6月29日的日期实例,则{{ conference_date | timeuntil }}将返回“4周”。

  使用可选参数,它是一个包含用作比较点的日期(而不是现在)的变量。 如果from_date包含2006年6月22日,则以下内容将返回“1周”:

{{ conference_date|timeuntil:from_date }}

  这里简单介绍一些常用的模板的过滤器,更多详见

更多内置过滤器(此链接页面最下面的内置过滤器):https://docs.djangoproject.com/en/1.11/ref/templates/builtins/#filters 

四、标签Tags

现在我们已经可以从后端通过模版系统替换掉前端的数据了,但是如果只是替换掉数据,确实不够灵活,比如,我们要想在前端页面展示可迭代对象name_list = ['王阔', '天琪', '傻强', '志晨', '健身哥']每个元素,你如和展示呢?

# 前端页面
<ul>
    <li>{{ name_list.0 }}</li>
    <li>{{ name_list.1 }}</li>
    <li>{{ name_list.2 }}</li>
    <li>{{ name_list.3 }}</li>
</ul>

这样写明显很low,我们要是可以用上for循环就好了。Django这么强大的框架,不可能想不到这点的,这里模版系统给我们提供了另一种标签,就是可以在html页面中进行for循环以及if判断等等。

标签看起来像是这样的: {% tag %}。标签比变量更加复杂:一些在输出中创建文本,一些通过循环或逻辑来控制流程,一些加载其后的变量将使用到的额外信息到模版中。与python语法不同的是:一些标签需要开始和结束标签 (例如{% tag %} ...标签 内容 ... {% endtag %})。

学习下面几种标签之前,我们要重新写一个url、views以及html,方便分类学习不与上面的变量产生冲突。

urls:
    url(r'^tags/',views.tags),


views:
def tags(request):
    num = 10
value = '' name_list = ['王阔', '天琪', '傻强', '志晨', '健身哥'] dic = {'name': '太白金星', 'age': 18} return render(request, 'tags.html', locals()) 先创建一个简单的tags.html即可。

for标签

基本语法:

{% for 变量 in render的可迭代对象 %}
        {{ 变量 }}
{% endfor %}

例如:
{% for foo in name_list %}
        {{ foo }}
{% endfor %}

便捷用法:

遍历每一个元素:  写个for,然后 tab键自动生成for循环的结构,循环很基础,就这么简单的用,没有什么break之类的,复杂一些的功能,你要通过js。可以利用{% for obj in list reversed %}反向完成循环。

遍历一个列表

<ul>
    {% for foo in name_list %}
        <li>{{ foo }}</li>
    {% endfor %}
</ul>

反向遍历一个列表

{% for foo in name_list reversed %}
        <li>{{ foo }}</li>
{% endfor %}

遍历一个字典:有items、keys、values参数

<ul>

    {% for key,value in dic.items %}
        <li>{{ key }}: {{ value }}</li>
    {% endfor %}

    {% for key in dic.keys %}
        <li>{{ key }}</li>
    {% endfor %}

    {% for value in dic %}
        <li>{{ value }}</li>
    {% endfor %}

</ul>

forloop的使用

模版系统给我们的for标签还提供了forloop的功能,这个就是获取循环的次数,有多种用法:

forloop.counter            当前循环的索引值(从1开始),forloop是循环器,通过点来使用功能
forloop.counter0           当前循环的索引值(从0开始)
forloop.revcounter         当前循环的倒序索引值(从1开始)
forloop.revcounter0        当前循环的倒序索引值(从0开始)
forloop.first              当前循环是不是第一次循环(布尔值)
forloop.last               当前循环是不是最后一次循环(布尔值)
forloop.parentloop         本层循环的外层循环的对象,再通过上面的几个属性来显示外层循环的计数等

我们通过name_list示例:

<ul>
    {% for foo in name_list %}

        <li>{{ forloop.counter }} {{ foo }}</li>
    {% endfor %}

    {% for foo in name_list %}
        {{ forloop.counter0 }}
        <li>{{ foo }}</li>
    {% endfor %}
</ul>

Django基础之:模版系统Django基础之:模版系统

 forloop.counter0数字与元素不在一行是因为我html标签摆放的问题,与方法无关。

{% for foo in name_list %}
    <li>{{ forloop.revcounter }} {{ foo }}</li>
 {% endfor %}

Django基础之:模版系统

{% for foo in name_list %}
        <li>{{ forloop.first }} {{ foo }}</li>
{% endfor %}

Django基础之:模版系统 

forloop.first、forloop.last多用于下面我们讲到if判断条件。 forloop.parentloop我们讲到if标签在演示。

for...empty...组合 

如果遍历的可迭代对象是空的或者就没有这个对象,利用这个组合可以提示用户。

<ul>        
    {% for foo in aaa %}
        <li>{{ foo }}</li>
    {% empty %}
        <li>查询的内容啥也没有</li>
    {% endfor %}

    {% for foo in value %}
        <li>{{ foo }}</li>
    {% empty %}
        <li>查询的内容啥也没有</li>
     {% endfor %}
</ul>

Django基础之:模版系统

if标签

基本语法:

 {% if %}会对一个变量求值,如果它的值是“True”(存在、不为空、且不是boolean类型的false值),对应的内容块会输出。

{% if 条件 %}
    结果  <!--不满足条件,不会生成这个标签-->
{% elif 条件 %}
    结果
{% else %}  <!--也是在if标签结构里面的-->
    结果
{% endif %}

elif和else一定要在if endif标签里面,设置多个elif或者没有elif、有没有else都可以。

示例:

{% if dic.age > 18 %}
    <p>可以干点儿该做的事儿了~</p>
{% elif dic.age < 18 %}
    <p>小孩子,懂什么</p>
{% else %}
    <p> 风华正茂的年龄~</p>
{% endif %}

条件也可以与过滤功能配合

% if name_list|length > 4 %}
    <p>列表元素超过4个</p>
{% else %}
    <p>列表元素太少!</p>
{% endif %}

条件也可以加逻辑运算符

if语句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判断,注意条件两边都有空格。

{% if name_list|length > 4 and '王阔' in name_list %}
    <p>条件都满足</p>
{% endif %}

with

,当你需要使用一个“昂贵的”方法(比如访问数据库)很多次的时候是非常有用的,记住!等号的左右不要加空格!!

{% with total=business.employees.count %}
    {{ total }} <!--只能在with语句体内用-->
{% endwith %}

{% with business.employees.count as total %}
    {{ total }}
{% endwith %}

forloop.first、forloop.last、forloop.parentloop

当时讲for循环时有三个方法,我们没有尝试,因为需要与if条件配合。

forloop.first

{% for foo in name_list %}
    {% if forloop.first %}
        {{ foo }}
    {% else %}
        <p>只有第一次循环打印</p>
    {% endif %}
{% endfor %}

Django基础之:模版系统

forloop.parentloop : 一定注意!他是返回本此循环的外层循环对象,这个对象可以调用forloop的各种方法进行获取相应的数据。

测试此方法,我们要在views函数中加一个数据类型:lis = [['A', 'B'], ['C', 'D'], ['E', 'F']]

{% for i in lis %}
     {% for j in i %}
{#         <p>{{ forloop.parentloop }}</p>#}
        <p>{{ forloop.parentloop.counter }} {{ forloop.counter }} {{ j }}</p>
     {% endfor %}
{% endfor %}

注意事项

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的值。

csrf_token标签

  这个标签是不是非常熟悉?之前我们以post方式提交表单的时候,会报错,还记得我们在settings里面的中间件配置里面把一个csrf的防御机制给注销了啊,本身不应该注销的,而是应该学会怎么使用它,并且不让自己的操作被forbiden,通过这个标签就能搞定。

接下来我们重写构建一个流程:

urls、views、html:

urlpatterns = [
    url(r'^login/',views.login),
]


from django.shortcuts import render, HttpResponse, redirect
def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        print(request.POST)  # 这里我们就不进行验证了,只是研究csrf_token
        return HttpResponse('登录成功!!!')


<!DOCTYPE html>
<html lang="zh-CN">
<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>Bootstrap 101 Template</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet">


</head>
<body>
<h3>登录页面</h3>

<form action="" method="post">
    用户名:<input type="text" name="username">
    密码:<input type="text" name="password">
    <button>提交</button>
</form>
</body>
</html>
View Code

相关文章:

  • 2018-04-02
  • 2018-03-29
  • 2018-01-25
  • 2018-03-26
  • 2018-11-21
  • 2021-10-17
  • 2021-08-15
  • 2019-10-11
猜你喜欢
  • 2021-10-01
  • 2019-09-23
  • 2021-10-01
  • 2021-10-01
  • 2018-01-25
  • 2021-08-21
  • 2021-10-01
  • 2019-09-22
相关资源
相似解决方案