一 模板简介
在刚刚介绍完的视图层中我们提到,浏览器发送的请求信息会转发给视图函数进行处理,而视图函数在经过一系列处理后必须要有返回信息给浏览器。如果我们要返回html标签、css等数据给浏览器进行渲染,我们可以在视图函数中这么做
def current_datetime(request): now = datetime.datetime.now() html = "<html><body>It is now %s.</body></html>" % now return HttpResponse(html)
上例所示,我们直接将HTML代码放到视图函数里,然后进行返回,这可以使我们很直观地看清楚浏览器从发送请求到看到前端界面内容的这个过程中视图函数的基本工作原理,但是这种将前端代码与后端代码耦合到了一起开发方式,会存在以下问题
1、程序的可维护性与可扩展性问题 前端界面一旦需要重新设计、修改,则必须对后端的Python代码进行相应的修改。
然而前端界面的修改往往比后端 Python 代码的修改要频繁得多,因此如果可以在不进行 Python 代码修改的情况下变更前端界面的设计,那将会方便得多。 2、开发效率问题 Python 代码编写和 HTML 设计是两项不同的工作,大多数专业的网站开发环境都将它们分配给不同的人员(甚至不同部门)来完成。
专门的程序员去编写 Python代码、专门的设计人员去制作模板,这两项工作同时进行,效率才是最高的
基于上述原因,将前端页面和Python的代码分离是一种不错的开发模式。 为此 Django专门提供了模板层来实现这种模式。
django的模板=HTML代码+模板语法
存放于templates目录下的html文件称之为模板文件,如果我们想要返回的html页面中的数据是动态的,那么必须在html页面中嵌入变量,这便用到了django的模板语法。
# 模板语法符号:
{{}} 变量相关 {%%} 逻辑相关
模板语法之模板传值
传python基本数据类型
python所有的数据类型都支持传递给html页面
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {#<p>{{ n }}</p>#} {# 模板语法的注释 这个注释前端浏览器检查是看不见的 #} <!-- 浏览器检查能够看到 --> <p>{{ f }}</p> <p>{{ s }}</p> <p>{{ li }}</p> <p>{{ dic }}</p> <p>{{ t }}</p> <p>{{ se }}</p> <p>{{ b }}</p> </body> </html>
views.py
def index(request): # python所有的数据类型都支持传递给html页面 n = 11 f = 11.11 s = 'hello world' li = [1,2,3,4,5] dic = {'username':'jason','password':123} t = (1,2,3,4,5,) se = {1,2,3,4,5,6} b = True # 方式一 给模板传递数据 (指名道姓地传) # return render(request,'index.html',{'n':n,'f':f}) # 方式二 给模板传递数据 #locals 会将当前名称空间中所有的变量名全部传递给html页面,这里应该是函数的名称空间,是一个局部的名称空间 return render(request,'index.html',locals())
传函数名
函数名会自动加括号执行,展示的是函数的返回值,模板语法不支持给函数传参
index.html
<p>{{ func }}</p>
views.py
def index(request): def func(): print('func被执行了') return 'from func' return render(request,'index.html',locals())
传类名和对象
传类名,也会自动加括号实例化
index.html
<p>{{ MyClass }}</p> <p>{{ MyClass.get_func }}</p> <p>{{ MyClass.get_cls }}</p> <p>{{ MyClass.get_self }}</p> <p>{{ obj }}</p> <p>{{ obj.get_func }}</p> <p>{{ obj.get_self }}</p> <p>{{ obj.get_cls }}</p>
views.py
def index(request): class MyClass(object): def get_self(self): return 'from self' @staticmethod def get_func(): return 'from func' @classmethod def get_cls(cls): return 'from cls' obj = MyClass() return render(request,'index.html',locals())
注意:模板语法不支持传参
取值
django模板语法取值,只有一种操作方式:句点符 .
<!--调用字符串对象的upper方法,注意不要加括号--> <p>{{ msg.upper }}</p> <!--取字典中k1对应的值--> <p>{{ dic.k1 }}</p> <!--取对象的name属性--> <p>{{ obj.name }}</p> <!--取列表的第2个元素,然后变成大写--> <p>{{ li.1.upper }}</p> <!--取列表的第3个元素,并取该元素的age属性--> <p>{{ li.2.age }}</p>
模板语法之过滤器
过滤器类似于python的内置函数,用来把视图传入的变量值加以修饰后再显示
具体语法: {{ 变量名|过滤器名:传给过滤器的参数 }}
|length |add |default |truncatechars |truncatewords |filesizeformat |slice |date |safe
#1、length 求数据的长度 # 它对字符串、列表、字典等容器类型都起作用,如果value是 ['a', 'b', 'c', 'd'],那么输出是4 {{ value|length }} #2、add 加法运算 {{ 5|add:10 }} #结果为5+10=15 {{ 'hank'|add:'DSB' }} # 作字符串拼接'hankDSB' #3、default #作用:如果一个变量值是False或者为空,使用default后指定的默认值,否则,使用变量本身的值,如果value=’‘则输出“nothing” {{ value|default:"nothing" }} #4、truncatechars #作用:如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾,如果value=”hello world“,则输出"hello...",注意8个字符也包含末尾的3个点 {{ value|truncatechars:8 }} #5、truncatewords #作用:同truncatechars,但truncatewords是按照单词截断,注意末尾的3个点不算作单词,如果value=”hello world 嘎嘎“,则输出"hello world ..." {{ value|truncatewords:2 }} #6、filesizeformat #作用:将值的格式化为一个"人类可读的"文件尺寸(如13KB、4.1 MB、102bytes等等),如果 file_size 是 12312312321,输出将会是 11.5 GB {{ file_size|filesizeformat }} #7、slice #作用:对输出的字符串进行切片操作,顾头不顾尾,如果value=“hank“,则输出"ha" {{ value|slice:"0:2" }} #8、date #作用:将日期按照指定的格式输出,如果value=datetime.datetime.now(),按照格式Y-m-d则输出2019-02-02 {{ value|date:"Y-m-d" }} #9、safe #作用:出于安全考虑,Django的模板会对HTML标签、JS等语法标签进行自动转义 {{ value|safe }}
#补充转义:
前后端取消转义
前端
|safe
后端
from django.utils.safestring import mark_safe
mark_safe('<h1>安全滴</h1>')
自定义过滤器
先完成以下前期准备工作:
1.在应用名下新建一个名字必须叫templatetags文件夹
2.在该文件夹内新建一个任意名称的py文件(eg:myfilter)
3.在该文件内 必须先写以下两句代码
from django.template import Library register = Library() #这里必须叫register
演示:
index.html
{% load myfilter %} {{ 10|my_sum:90 }}
myfilter.py
from django.template import Library register = Library() #自定义过滤器 @register.filter(name='my_sum') def index(a,b): return a + b
模板语法之标签
常用标签之for标签
#1、遍历每一个元素: {% for person in person_list %} <p>{{ person.name }}</p> {% endfor %} #2、可以利用{% for obj in list reversed %}反向循环。 #3、遍历一个字典: {% for key,val in dic.items %} <p>{{ key }}:{{ val }}</p> {% endfor %} #4、循环序号可以通过{{ forloop }}显示 forloop.counter 当前循环的索引值(从1开始) forloop.counter0 当前循环的索引值(从0开始) forloop.revcounter 当前循环的倒序索引值(从1开始) forloop.revcounter0 当前循环的倒序索引值(从0开始) forloop.first 当前循环是第一次循环则返回True,否则返回False forloop.last 当前循环是最后一次循环则返回True,否则返回False forloop.parentloop 本层循环的外层循环 #5、for标签可以带有一个可选的{% empty %} 从句,在变量person_list为空或者没有被找到时,则执行empty子句 {% for person in person_list %} <p>{{ person.name }}</p> {% empty %} <p>sorry,no person here</p> {% endfor %}
{% for foo in l %} <!--若l = [1,2,3,4,5,6]--> {% if forloop.first %} <p>这是我的第一次</p> {% elif forloop.last %} <p>这是最后一次了啊~</p> {% else %} <p>{{ foo }}</p> {% endif %} {% empty %} <!--若l = []--> <p>for循环的对象内部没有值</p> {% endfor %}
常用标签之with标签
# with标签用来为一个复杂的变量名起别名,如果变量的值来自于数据库,在起别名后只需要使用别名即可,无需每次都向数据库发送请求来重新获取变量的值 {% with li.1.upper as v %} {{ v }} #别名只能在with内使用 #{{li.l.upper}} {% endwith %}
常用标签之if标签
# 1、注意: {% if 条件 %}条件为真时if的子句才会生效,条件也可以是一个变量,if会对变量进行求值,在变量值为空、或者视图没有为其传值的情况下均为False # 2、具体语法 {% if num > 100 or num < 0 %} <p>无效</p> {% elif num > 80 and num < 100 %} <p>优秀</p> {% else %} <p>凑活吧</p> {% endif %} #3、if语句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判断。
自定义标签
先完成以下前期准备工作:(和自定义过滤器一样)
1.在应用名下新建一个名字必须叫templatetags文件夹
2.在该文件夹内新建一个任意名称的py文件(eg:mytag)
3.在该文件内 必须先写以下两句代码
from django.template import Library register = Library()
演示:
index.html
#自定义过滤器的使用 {% load mytag %} {% my_tag 1 2 'hello' %}
mytag.py
from django.template import Library register = Library() @register.simple_tag(name='my_tag') def xxx(a,b,c): return '%s?%s?%s?'%(a,b,c)
#输出结果:
1?2?'hello'
自定义标签与自定义过滤器的区别
1、自定义过滤器只能传两个参数,而自定义标签却可以传多个参数
2、自定义的过滤器可以在逻辑语句中使用,而自定义的标签不可以
自定义inclusion_tag
在html页面上调用一个inclusion_tag,它会调用一个函数,这个函数把数据渲染到一个小页面上,然后把小页面的数据再传给html页面上来.
index.html
<!--自定义inclusion_tag的使用--> {% load mytag %} {% my 6 %}
mytag.py
from django.template import Library
register = Library()
@register.inclusion_tag('demo.html',name='my') def index1(n): l = [] for i in range(n): l.append(i) #将列表传递给demo.html return locals()
demo.html
<ul> {% for foo in l %} <li>{{ foo }}</li> {% endfor %} </ul>
模板的继承与派生之extends,block标签
某一个页面大部分区域都是公用的,那这个页面就可以作为模板页面.
#作用:在一个模板文件中,引入/重用另外一个模板文件的内容 {% extends "模版名称" %} #extends可以搭配一个block标签,用于在继承的基础上定制新的内容
当别人继承这个页面之后 如何修改对应的区域的内容呢?
步骤1 先在模板页面上通过block实现划定区域
{% block content %} 模板页面内容 {% endblock %}
步骤2 子页面中先导入整个模板
{% extends '模板页面.html'%} 修改特定的区域 通过实现划定好的区域名称 {% block content %} 子页面内容 {% endblock %}
通常情况下 模板页面页面应该起码有三块区域
{% block css %} 模板页面内容 {% endblock %} {% block content %} 模板页面内容 {% endblock %} {% block js %} 模板页面内容 {% endblock %} # 模板的block块越多 可扩展性越高
还支持子页面调用父页面对应区域的内容 并且可以无限次调用.
如果在content里面调,就是调用的父页面content的内容,在css里调,就是调用的就是父页面css的内容
{{ block.super }}
模板的导入
在一个模板文件中,引入/重用另外一个模板html文件的内容
这个html页面通常都不是完整的,只是一个局部样式
{% include '模版名称' %}
关于模块导入与继承的实际应用案例:
url.py
#模板的继承 url(r'^mb/',views.mb), url(r'^register/',views.register), url(r'^login/',views.login)
views.py
def mb(request): return render(request,'mb.html') def login(request): return render(request,'login.html') def register(request): return render(request,'register.html')
mb.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script> {% block css %} {% endblock %} </head> <body> <nav class="navbar navbar-inverse"> <div class="container-fluid"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <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="#">Brand</a> </div> <!-- Collect the nav links, forms, and other content for toggling --> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li class="active"><a href="#">Link <span class="sr-only">(current)</span></a></li> <li><a href="#">Link</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><a href="#">Separated link</a></li> <li role="separator" class="divider"></li> <li><a href="#">One more separated link</a></li> </ul> </li> </ul> <form class="navbar-form navbar-left"> <div class="form-group"> <input type="text" class="form-control" placeholder="Search"> </div> <button type="submit" class="btn btn-default">Submit</button> </form> <ul class="nav navbar-nav navbar-right"> <li><a href="#">Link</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><a href="#">Separated link</a></li> </ul> </li> </ul> </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav> <div class="container-fluid"> <div class="row"> <div class="col-md-3"> <div class="list-group"> <a href="/mb/" class="list-group-item active"> 首页 </a> <a href="/register/" class="list-group-item">注册</a> <a href="/login/" class="list-group-item">登录</a> <a href="#" class="list-group-item">Porta ac consectetur ac</a> <a href="#" class="list-group-item">Vestibulum at eros</a> </div> </div> <div class="col-md-9"> <div class="panel panel-primary"> <div class="panel-heading"> <h3 class="panel-title">Panel title</h3> </div> <div class="panel-body"> {% block content %} <div class="jumbotron"> <h1>Hello, world!</h1> <p>...</p> <p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a></p> </div> {% endblock %} </div> </div> </div> </div> </div> {% block js %} {% endblock %} </body> </html> mb.html
login.html
{% extends 'mb.html' %} {% block css %} <style> h2 { color: red; } </style> {% endblock %} {% block content %} {% include 'left.html' %} <h2 class="text-center">登录页面</h2> <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-primary"> </form> {{ block.super }} {% endblock %} {% block js %} {# <script>#} {# alert(123)#} {# </script>#} {% endblock %} login.html
register.html
{% extends 'mb.html' %} {% block css %} <style> h2 { color: red; } </style> {% endblock %} {% block content %} {% include 'left.html' %} <h2 class="text-center">登录页面</h2> <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-primary"> </form> {{ block.super }} {% endblock %} {% block js %} {# <script>#} {# alert(123)#} {# </script>#} {% endblock %} login.html
left.html
<p>这是一个好看的页面组件</p>