目录
一、模板介绍
二、定义模板
三、模板继承
实例:三层继承结构
四、HTML转义
五、csrf
一、模板介绍
-
作为Web框架,Django提供了模板,可以很便利的动态生成HTML
-
模版系统致力于表达外观,而不是程序逻辑
-
模板的设计实现了业务逻辑(view)与显示内容(template)的分离,一个视图可以使用任意一个模板,一个模板可以供多个视图使用
-
模板包含
-
HTML的静态部分
-
动态插入内容部分
-
-
Django模板语言,简写DTL,定义在django.template包中
-
由startproject命令生成的settings.py定义关于模板的值:
-
DIRS定义了一个目录列表,模板引擎按列表顺序搜索这些目录以查找模板源文件
-
APP_DIRS告诉模板引擎是否应该在每个已安装的应用中查找模板
-
-
常用方式:在项目的根目录下创建templates目录,设置DIRS值
DIRS=[os.path.join(BASE_DIR,"templates")]
二、定义模板
-
模板语言包括
-
变量
-
标签 { % 代码块 % }
-
过滤器
-
注释{# 代码或html #}
-
变量
-
语法:
{
{ variable }}
-
当模版引擎遇到一个变量,将计算这个变量,然后将结果输出
-
变量名必须由字母、数字、下划线(不能以下划线开头)和点组成
-
当模版引擎遇到点("."),会按照下列顺序查询:
-
字典查询,例如:foo["bar"]
-
属性或方法查询,例如:foo.bar
-
数字索引查询,例如:foo[bar]
-
-
如果变量不存在, 模版系统将插入'' (空字符串)
-
在模板中调用方法时不能传递参数
标签
-
语法:{ % tag % }
-
作用
-
在输出中创建文本
-
控制循环或逻辑
-
加载外部信息到模板中供以后的变量使用
-
for标签
{ %for ... in ...%} 循环逻辑 { {forloop.counter}}表示当前是第几次循环 { %empty%} 给出的列表为或列表不存在时,执行此处 { %endfor%}
-
if标签
{ %if ...%} 逻辑1 { %elif ...%} 逻辑2 { %else%} 逻辑3 { %endif%}
-
comment标签
{ % comment % } 多行注释 { % endcomment % }
-
include:加载模板并以标签内的参数渲染
{ %include "foo/bar.html" % }
-
url:反向解析
{ % url 'name' p1 p2 %}
-
csrf_token:这个标签用于跨站请求伪造保护
{ % csrf_token %}
-
with标签,起别名
-
{% with s.hobby.3 as sh %}{ { sh }}{%endwith%}
-
布尔标签:and、or,and比or的优先级高
-
block、extends:详见“模板继承”
-
autoescape:详见“HTML转义”
-
-
过滤器
-
语法:{ { 变量|过滤器 }},例如{ { name|lower }},表示将变量name的值变为小写输出
-
使用管道符号 (|)来应用过滤器
-
通过使用过滤器来改变变量的计算结果
-
可以在if标签中使用过滤器结合运算符
if list1|length > 1
-
过滤器能够被“串联”,构成过滤器链
name|lower|upper
-
过滤器可以传递参数,参数使用引号包起来
list|join:", "
-
default:如果一个变量没有被提供,或者值为false或空,则使用默认值,否则使用变量的值
value|default:"什么也没有"
-
date:根据给定格式对一个date变量格式化
value|date:'Y-m-d H:M:S'
-
escape:详见“HTML转义”
-
filesizeformate:文件大小,将数字自动转化为文件大小的格式
{
{filesize|filesizeformate}}
-
safe:转义,加上safe认为是安全的可以转义
sl = '<h1>如花</h1>'{
{sl|safe}}
自定义过滤器、标签、inclusion_tag
三步走:
1. 在引用下创建一个名字必须是 templatetags 的文件夹。
2. 在该文件夹下创建任意名字的 .py 文件。 eg:mytag.py
3. 在py文件下必须先写下面代码
from django import template
register = template.Library()
from django import templateregister = template.Library()# 自定义过滤器
@register.filter(name='xxx')
def my_sum(v1, v2):return v1 + v2# 自定义标签
@register.simple_tag(name='ooo')
def index(a, b, c, d):return '%s-%s-%s-%s' % (a, b, c, d)# 自定义inclusion_tag
"""内部原理:1.先定义一个方法2.在页面上调用这个方法,并且可以传值3.该方法会生成一些数据传递给指定的html4.之后将html渲染好的结果方法调用的页面位置
"""@register.inclusion_tag('upload.html')
def left(n):data = ['第{}项'.format(i) for i in range(n)]return locals()
# 会将结果传递到upload.html进行渲染
{% load mytag %}<h1>自定义过滤器使用</h1>
{
{22|xxx:33}}
打印:55<h1>自定义标签使用(多个参数空格隔开)</h1>
{% ooo 'xiaohu' 12 12 12 %}
打印:xiaohu-12-12-12<h1>自定义inclusion_tag使用</h1>
{% left 5 %}
注释
-
单行注释
{#...#}
-
注释可以包含任何模版代码,有效的或者无效的都可以
{# { % if foo % }bar{ % else % } #}
-
使用comment标签注释模版中的多行内容
三、模板继承
-
模板继承可以减少页面内容的重复定义,实现页面内容的重用
-
典型应用:网站的头部、尾部是一样的,这些内容可以定义在父模板中,子模板不需要重复定义
-
block标签:在父模板中预留区域,在子模板中填充
-
extends继承:继承,写在模板文件的第一行
-
定义父模板base.html
{ %block block_name%}
这里可以定义默认值
如果不定义默认值,则表示空字符串
{ %endblock%}
-
定义子模板index.html
{ % extends "base.html" %}
-
在子模板中使用block填充预留区域
{ %block block_name%}
实际填充内容
{ %endblock%}
说明
-
如果在模版中使用extends标签,它必须是模版中的第一个标签
-
不能在一个模版中定义多个相同名字的block标签
-
子模版不必定义全部父模版中的blocks,如果子模版没有定义block,则使用了父模版中的默认值
-
如果发现在模板中大量的复制内容,那就应该把内容移动到父模板中
-
使用可以获取父模板中block的内容
-
为了更好的可读性,可以给endblock标签一个名字
{ % block block_name %}
区域内容
{ % endblock block_name %}
实例:三层继承结构
-
三层继承结构使代码得到最大程度的复用,并且使得添加内容更加简单
-
如下图为常见的电商页面
1.创建根级模板
-
名称为“base.html”
-
存放整个站点共用的内容
<!DOCTYPE html>
<html>
<head><title>{%block title%}{%endblock%} 水果超市</title>
</head>
<body>
top--{
{logo}}
<hr/>
{%block left%}{%endblock%}
{%block content%}{%endblock%}
<hr/>
bottom
</body>
</html>
2.创建分支模版
-
继承自base.html
-
名为“base_***.html”
-
定义特定分支共用的内容
-
定义base_goods.html
{%extends 'temtest/base.html'%}
{%block title%}商品{%endblock%}
{%block left%}
<h1>goods left</h1>
{%endblock%}
-
定义base_user.html
{%extends 'temtest/base.html'%}
{%block title%}用户中心{%endblock%}
{%block left%}
<font color='blue'>user left</font>
{%endblock%}
-
定义index.html,继承自base.html,不需要写left块
{%extends 'temtest/base.html'%}
{%block content%}
首页内容
{%endblock content%}
3.为具体页面创建模板,继承自分支模板
-
定义商品列表页goodslist.html
{%extends 'temtest/base_goods.html'%}
{%block content%}
商品正文列表
{%endblock content%}
-
定义用户密码页userpwd.html
{%extends 'temtest/base_user.html'%}
{%block content%}
用户密码修改
{%endblock content%}
4.视图调用具体页面,并传递模板中需要的数据
-
首页视图index
logo='welcome to itcast'
def index(request):return render(request, 'temtest/index.html', {'logo': logo})
-
商品列表视图goodslist
def goodslist(request):return render(request, 'temtest/goodslist.html', {'logo': logo})
-
用户密码视图userpwd
def userpwd(request):return render(request, 'temtest/userpwd.html', {'logo': logo})
5.配置url
from django.conf.urls import url
from . import views
urlpatterns = [url(r'^$', views.index, name='index'),url(r'^list/$', views.goodslist, name='list'),url(r'^pwd/$', views.userpwd, name='pwd'),
]
四、HTML转义
-
Django对字符串进行自动HTML转义,如在模板中输出如下值:
视图代码:
def index(request):return render(request, 'temtest/index2.html',{'t1': '<h1>hello</h1>'})
模板代码:
{
{t1}}
-
显示效果如下图:
-
-
会被自动转义的字符
-
html转义,就是将包含的html标签输出,而不被解释执行,原因是当显示用户提交字符串时,可能包含一些攻击性的代码,如js脚本
-
Django会将如下字符自动转义:
< 会转换为<
?
> 会转换为>
?
' (单引号) 会转换为'
?
" (双引号)会转换为 "
?
& 会转换为 &
-
当显示不被信任的变量时使用escape过滤器,一般省略,因为Django自动转义
{
{t1|escape}}
关闭转义
-
对于变量使用safe过滤器
{
{ data|safe }}
-
对于代码块使用autoescape标签
{ % autoescape off %}
{
{ body }}
{ % endautoescape %}
-
标签autoescape接受on或者off参数
-
自动转义标签在base模板中关闭,在child模板中也是关闭的
字符串字面值
-
手动转义
{ { data|default:"<b>123</b>" }}
-
应写为
{ { data|default:"<b>123</b>" }}
五、csrf
-
全称Cross Site Request Forgery,跨站请求伪造
-
某些恶意网站上包含链接、表单按钮或者JavaScript,它们会利用登录过的用户在浏览器中的认证信息试图在你的网站上完成某些操作,这就是跨站攻击
防csrf的使用
-
在django的模板中,提供了防止跨站攻击的方法,使用步骤如下:
-
step1:在settings.py中启用'django.middleware.csrf.CsrfViewMiddleware'中间件,此项在创建项目时,默认被启用
-
step2:在csrf1.html中添加标签
<form>
{% csrf_token %}
...
</form>
取消保护
-
如果某些视图不需要保护,可以使用装饰器csrf_exempt,模板中也不需要写标签,修改csrf2的视图如下
from django.views.decorators.csrf import csrf_exempt
?
@csrf_exempt
def csrf2(request):uname=request.POST['uname']return render(request,'booktest/csrf2.html',{'uname':uname})
保护原理
-
加入标签后,可以查看源代码,发现多了如下代码
<input type='hidden' name='csrfmiddlewaretoken' value='nGjAB3Md9ZSb4NmG1sXDolPmh3bR2g59' />
-
在浏览器的调试工具中,通过network标签可以查看cookie信息
-
本站中自动添加了cookie信息,如下图
-
查看跨站的信息,并没有cookie信息,即使加入上面的隐藏域代码,发现又可以访问了
-
结论:django的csrf不是完全的安全
-
当提交请求时,中间件'django.middleware.csrf.CsrfViewMiddleware'会对提交的cookie及隐藏域的内容进行验证,如果失败则返回403错误