Django 模板(Template)
1. 模板简介
2. 模板语言 DTL
3. 模板继承
4. HTML 转义
5. CSRF
1. 模板简介
作为 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")]
模板处理
Django 处理模板分为两个阶段:
- Step1 加载:根据给定的标识找到模板然后预处理,通常会将它编译好放在内存中
loader.get_template(template_name) # 返回一个Template对象
- Step2 渲染:使用Context数据对模板插值并返回生成的字符串
Template对象的render(RequestContext)方法,使用context渲染模板
- 加载渲染完整代码:
from django.template import loader, RequestContext
from django.http import HttpResponse def index(request):
tem = loader.get_template('temtest/index.html')
context = RequestContext(request, {})
return HttpResponse(tem.render(context))
快捷函数
为了减少加载模板、渲染模板的重复代码,Django 提供了快捷函数:
- render_to_string("")
- render(request, '模板', context)
from django.shortcuts import render def index(request):
return render(request, 'temtest/index.html')
2. 模板语言 DTL
模板语言包括:
- 变量 {{ 变量名 }}
- 标签 { % 代码块 % }
- 过滤器
- 注释 {# 代码或html #}
变量
语法:
{{ variable }}
- 当模版引擎遇到一个变量,将计算这个变量,然后将结果输出。
- 变量名只能由字母、数字、下划线(不能以下划线开头)和点组成。
- 当模版引擎遇到点("."),会按照下列顺序查询:
- 字典查询,例如:foo["bar"]
- 属性或方法查询,例如:foo.bar
- 数字索引查询,例如:foo[bar]
- 如果变量不存在, 模版系统将插入空字符串。
- 在模板中调用方法时不能传递参数。
范例:在模板中调用对象的方法
- 在 models.py 中定义类 HeroInfo 类:
from django.db import models class HeroInfo(models.Model):
...
def showName(self):
return self.hname
- 在 views.py 中传递 HeroInfo 对象:
from django.shortcuts import render
from models import * def index(request):
hero = HeroInfo(hname='abc')
context = {'hero': hero}
return render(request, 'temtest/detail.html', context)
- 在模板 detail.html 中调用对象的方法:
{{hero.showName}}
标签
语法:
{% 代码块 %}
作用:
- 在输出中创建文本
- 控制循环或逻辑
- 加载外部信息到模板,供以后的变量使用
for 标签
语法:
{% for ... in ... %}
# 循环体中的逻辑
{{forloop.counter}} # 表示当前是第几次循环(从1开始)
{% empty %}
# 给出的列表为空或列表不存在时,执行此处。类似于 else
{% endfor %} # for 循环的结束标识
示例:
<body>
{% for hero_obj in hero %}
{{ forloop.counter }}: {{hero_obj.show}}<br/>
{% empty %}
<h2>啥也没找到...</h2>
{% endfor %}
</body>
1: 郭靖
2: 黄蓉
3: 比伯
4: 王嘉尔
5: 欧阳锋
if 标签
语法:
{% if ... %}
逻辑1
{% elif ... %}
逻辑2
{% else %}
逻辑3
{% endif %}
示例:
<body>
<ul>
{% for hero_obj in hero %}
{% if forloop.counter|divisibleby:"2" %} {# 当前行数是否能整除2:偶数行为蓝色,奇数行为红色 #}
<li style="color: blue">{{ forloop.counter }}: {{hero_obj.show}}</li>
{% else %}
<li style="color: red">{{ forloop.counter }}: {{hero_obj.show}}</li>
{% endif %}
{% empty %}
<h2>啥也没找到...</h2>
{% endfor %}
</ul>
</body>
include 标签
加载模板并以标签内的参数渲染:
{% include "foo/bar.html" %}
url 标签
反向解析:在模板中的链接部分不使用硬编码,而是使用反向解析根据 urls 配置生成访问地址。
原先的访问方式:根据访问地址去匹配 urls。
范例 1:不带参数
项目 urls.py:
url(r'^hero_book/', include('hero_book.urls', namespace='hero_book')),
应用 urls.py:
url(r"^\d+$", views.show, name='show'),
views.py:
def show(request):
return render(request, "hero_book/show.html")
模板 index.html:
<a href="{%url 'hero_book:show' %}">show</a> # 根据 urls 配置自动生成符合 hero_book/\d+ 的访问地址
模板 show.html:
<body>
show page
</body>
执行效果:
范例 2:带参数
应用 urls.py:
url(r"^(\d+)$", views.show, name='show'), # 接收反向解析中传递过来的参数
模板 index.html:
<a href="{%url 'hero_book:show' 123 %}">show</a> {# 传递数字123作为参数 #}
模板 show.html:
show page {{ id }}
执行效果:
csrf_token 标签
跨站请求伪造保护:
{% csrf_token %}
布尔标签
- and、or
- and 比 or 的优先级高
注释
- 多行注释:comment 标签
{% comment %}
多行注释
{% endcomment %}
- 单行注释:
{# ... #}
- 注释可以包含任何模版代码,有效的或者无效的都可以:
{# { % if foo % }bar{ % else % } #}
过滤器
语法:
{{ 变量|过滤器 }}
- 使用管道符号 | 来应用过滤器。
- 通过使用过滤器来改变变量的计算结果。
- 可以在 if 标签中使用过滤器结合运算符。
示例:
{% if list1|lenth > 1 %} # 判断变量 list1 的长度是否大于1
{{ name|lower }} # 表示将变量 name 的值变为小写输出
- 过滤器能够被“串联”,构成过滤器链:
name|lower|upper
- 过滤器可以传递参数,参数使用引号包起来
list|join:", "
- default:如果一个变量没有被提供、值为 false 或空,则使用默认值;否则使用变量的值:
value|default:"什么也没有"
- date:根据给定格式对一个 date 变量格式化:
value|date:'Y-m-d'
3. 模板继承
模板继承可以减少页面内容的重复定义,实现页面内容的重用。
典型应用:网站的头部、尾部是一样的,这些内容可以定义在父模板中,子模板不需要重复定义。
语法
- block 标签:在父模板中预留区域,在子模板中填充。
{% block block_name %}
这里可以定义默认值
如果不定义默认值,则表示空字符串
{ %endblock%}
- extends 标签:在子模板中继承父模板,写在模板文件的第一行。
{% extends "父模板名称" %}
- 在子模板中使用 block 填充预留区域:
{% block block_name %}
实际填充内容
{% endblock %}
- 为了更好的可读性,可以给 endblock 标签一个名字:
{% block block_name %}
区域内容
{% endblock block_name %}
使用说明
- 如果在模版中使用 extends 标签,它必须是模版中的第一个标签。
- 不能在一个模版中定义多个相同名字的 block 标签。
- 子模版不必定义全部父模版中的 blocks,如果子模版没有定义 block,则使用父模版中的默认值。
- 如果发现在模板中有大量的可重用内容,就应该把内容移动到父模板中。
范例:二层继承
views.py:
def child(request):
return render(request, "hero_book/child.html")
urls.py:
url(r"^child/$", views.child, name="child"),
定义父模板 base.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
{% block head %}
{% endblock %}
</head>
<body>
{% block content1 %}
<h1>content1</h1> {# content1的默认值 #}
{% endblock %}
<hr/>
{% block content2 %}
<h1>content2</h1> {# content2的默认值 #}
{% endblock %}
</body>
</html>
效果 1:子模板 child.html 只继承,不填充。
{% extends "hero_book/base.html" %}
效果 2:子模板 child.html 继承并填充。
{% extends "hero_book/base.html" %} {% block content1 %} {# 替换父模板中的默认值 #}
<h1>child content1</h1>
{% endblock content1 %}
范例:三层继承
三层继承结构使代码得到最大程度的复用,并且使得添加内容更加简单。
如下图为常见的电商页面:
实现示例:用户页
定义 base.html,重用页面的头尾内容:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
{% block head %}
{% endblock %}
</head>
<body>
<h1>TOP</h1>
<hr/>
{% block content %}
{% endblock content %}
<hr/>
<h1>BOTTOM</h1>
</body>
</html>
定义 user_base.html 继承 base.html,重用用户页的布局和左侧导航:
{% extends "hero_book/base.html" %} {% block content %}
<table border="1">
<tr>
<td height="300">用户</br>导航</td>
<td>{% block user_content %}{% endblock %}</td>
</tr>
</table>
{% endblock content %}
定义 user1.html 和 user2.html 分别继承 user_base.html,展示具体的用户信息:
{% extends "hero_book/user_base.html" %} {% block user_content %}
用户信息1
{% endblock user_content %}
{% extends "hero_book/user_base.html" %} {% block user_content %}
用户信息2
{% endblock user_content %}
views.py:
def user1(request):
return render(request, "hero_book/user1.html") def user2(request):
return render(request, "hero_book/user2.html")
urls.py:
url(r"^user1/$", views.user1, name="user1"),
url(r"^user2/$", views.user2, name="user2"),
执行效果:
4. HTML 转义
Django 会对视图函数传递的字符串自动进行 HTML 转义,如在模板中输出如下值:
# 视图代码
def index(request):
return render(request, 'temtest/index2.html', {'t1': '<h1>hello</h1>'}) # 模板代码
{{t1}}
网页显示效果如下:
<h1>hello</h1>
会被自动转义的字符
HTML 转义,就是将包含的 HTML 标签输出,而不被解释执行。原因是当显示用户提交字符串时,可能包含一些攻击性的代码,如 js 脚本。
Django 会将如下字符自动转义:
< 会转换为 < > 会转换为 > ' (单引号) 会转换为 ' " (双引号) 会转换为 " & 会转换为 &
当显示不被信任的变量时使用 escape 过滤器,一般省略,因为 Django 自动转义:
{{ t1|escape }}
关闭转义
对于变量使用 safe 过滤器:
{{ data|safe }}
对于代码块使用 autoescape 标签:
{% autoescape off %}
{{ body }}
{% endautoescape %}
- 标签 autoescape 接受 on 或者 off 参数。
- 自动转义标签如果在 base 模板中关闭,那么在 child 模板中也是关闭的。
字符串字面值
以下代码会解释输出加粗的 123:
{{ data|default:"<b>123</b>" }}
手动转义应写为:
{{ data|default:"<b>123</b>" }}
5. CSRF
跨站请求伪造(CSRF:Cross-site request forgery),简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并运行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去运行。这利用了 web 中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。
例子
假如一家银行用以运行转账操作的 URL 地址如下:http://www.examplebank.com/withdraw?account=AccoutName&amount=1000&for=PayeeName
那么,一个恶意攻击者可以在另一个网站上放置如下代码: <img src="http://www.examplebank.com/withdraw?account=Alice&amount=1000&for=Badman">
如果有账户名为 Alice 的用户访问了恶意站点,而她之前刚访问过银行不久,登录信息尚未过期,那么她就会损失 1000 资金。
这种恶意的网址可以有很多种形式,藏身于网页中的许多地方。此外,攻击者也不需要控制放置恶意网址的网站。例如他可以将这种地址藏在论坛,博客等任何用户生成内容的网站中。这意味着如果服务端没有合适的防御措施的话,用户即使访问熟悉的可信网站也有受攻击的危险。
透过例子能够看出,攻击者并不能通过CSRF攻击来直接获取用户的账户控制权,也不能直接窃取用户的任何信息。他们能做到的,是欺骗用户浏览器,让其以用户的名义运行操作。
CSRF 演示
创建视图 csrf1 用于展示表单,csrf2 用于接收 post 请求:
def csrf1(request):
return render(request, 'hero_book/csrf1.html') def csrf2(request):
uname = request.POST['uname']
return render(request, 'hero_book/csrf2.html', {'uname': uname})
配置 url:
url(r'^csrf1/$', views.csrf1),
url(r'^csrf2/$', views.csrf2),
csrf1.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="post" action="/hero_book/csrf2/">
<input name="uname"><br>
<input type="submit" value="提交"/>
</form>
</body>
</html>
csrf2.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{{ uname }}
</body>
</html>
在浏览器中查看访问效果,报错如下:
- 原因: Django 自带了防 CSRF 的中间件,需注释掉该功能才能访问(只要是 POST 请求均有此限制)。
- 解决方法:将 settings.py 中的中间件代码 'django.middleware.csrf.CsrfViewMiddleware' 注释掉,即可正常提交 POST 请求。
此时产生会产生 CSRF 问题:
查看 csrf1 的源代码,复制粘贴成本地的另一个 HTML 文件,访问查看效果:正常访问。
防 CSRF 的使用
在 Django 的模板中,提供了防止跨站攻击的方法,使用步骤如下:
- step1:在 settings.py 中启用 'django.middleware.csrf.CsrfViewMiddleware' 中间件(此项在创建项目时,默认被启用)。
- step2:在csrf1.html 中添加标签:
<form method="post" action="/hero_book/csrf2/">
{% csrf_token %}
<input name="uname">
<input type="submit" value="提交"/>
</form>
- step3:测试刚才的两个请求,发现 csrf1.html 正常提交 POST 请求,而跨站(本地 HTML 文件)的请求被拒绝了,效果如下图:
取消保护
如果某些视图不需要保护,可以使用装饰器 @csrf_exempt,模板中也不需要写标签,修改 csrf2 的视图如下:
from django.views.decorators.csrf import csrf_exempt @csrf_exempt
def csrf2(request):
uname = request.POST['uname']
return render(request, 'hero_bookt/csrf2.html', {'uname': uname})
再次运行之前的两个请求,发现都可以请求。
保护原理
加入 {% csrf_token %} 标签后,可以查看源代码,发现多了如下代码:
<input type='hidden' name='csrfmiddlewaretoken' value='nGjAB3Md9ZSb4NmG1sXDolPmh3bR2g59' />
在浏览器的调试工具中,通过 network 标签可以查看 cookie 信息,发现本站中自动添加了 cookie 信息,如下图:
当提交 POST 请求时,中间件 'django.middleware.csrf.CsrfViewMiddleware' 会对提交的 cookie 及隐藏域的内容进行验证,如果失败则返回 403 错误。
查看跨站的信息,并没有 cookie 信息,但当加入上面的隐藏域代码,发现又可以访问了。结论:Django 的 CSRF 不是完全的安全。
Django 模板(Template)的更多相关文章
- django模板(template)
模板层(template) 你可能已经注意到我们在例子视图中返回文本的方式有点特别. 也就是说,HTML被直接硬编码在 Python代码之中. 1 2 3 4 def current_datetime ...
- [django]模板template原理
django 中的render和render_to_response()和locals(): http://www.cnblogs.com/wangchaowei/p/6750512.html 什么是 ...
- Django——模板层(template)(模板语法、自定义模板过滤器及标签、模板继承)
前言:当我们想在页面上给客户端返回一个当前时间,一些初学者可能会很自然的想到用占位符,字符串拼接来达到我们想要的效果,但是这样做会有一个问题,HTML被直接硬编码在 Python代码之中. 1 2 3 ...
- Django模板语言(Template)
1.变量 变量相关用 { { } } 逻辑相关用{% %} 2.Filter过滤器 (1)default 如果一个变量是false或者为空,使用给定的默认值. 否则,使用变量的值. {{ va ...
- django模板语言之Template
当前端的一些数据需要后端传送过来并展示时,用到了django的模板语言,模板语言的作用就是,在后端把一些处理好的数据,通过模板语言所规定的格式,通过render渲染,放到前端页面固定的位置展示.这之间 ...
- Django之模板Template
模板介绍 作为Web框架,Django提供了模板,可以很便利的动态生成HTML 模版系统致力于表达外观,而不是程序逻辑 模板的设计实现了业务逻辑(view)与显示内容(template)的分离,一个视 ...
- Django之模板(Template)
Django模板系统 官方文档 每一个Web框架都需要一种很便利的方法用于动态生成HTML页面. 最常见的做法是使用模板. 模板包含所需HTML页面的静态部分,以及一些特殊的模版语法,用于将动态内容插 ...
- Django 模板系统(template)
介绍 官方文档 常用模板语法 只需要记两种特殊符号: {{ }} 和 {% %} 变量相关的用{{}} 逻辑相关的用{%%} 变量 {{ 变量名 }} 变量名由字母数字和下划线组成. 点(.)在模 ...
- Django框架简介及模板Template,filter
Django框架简介 MVC框架和MTV框架 MVC,全名是Model View Controller,是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model).视图(View) ...
- Django模板系统:Template
一.模板常用语法 1.1 变量 符号:{{ }} 表示变量,在模板渲染的时候替换成值 使用方式:{{ 变量名 }}:变量名由字母数字和下划线组成 点(.)在模板语言中有特殊的含义,用来获取对象的相应属 ...
随机推荐
- # PyComCAD介绍及开发方法
项目地址:https://github.com/JohnYang1210/PycomCAD 1.综述 提到Autocad在工业界的二次开发,VB或者Lisp可能作为常用的传统的编程语言.但是,Py ...
- 基于docker部署skywalking实现全链路监控
一.概述 简介 skywalking是一个开放源码的,用于收集.分析,聚合,可视化来自于不同服务和本地基础服务的数据的可观察的平台,skywalking提供了一个简单的方法来让你对你的分布式系统甚至是 ...
- Spring中的依赖查找和依赖注入
作者:Grey 原文地址: 语雀 博客园 依赖查找 Spring IoC 依赖查找分为以下几种方式 根据 Bean 名称查找 实时查找 延迟查找 根据 Bean 类型查找 单个 Bean 对象 集合 ...
- 后端程序员之路 39、一个Protocol Buffer实例
实际工作的Protocol Buffer使用经验 # 写proto文件- 协议版本 项目用的是protobuf2,所以要指定 syntax = "proto2";- 包名 pack ...
- 更改EFI分区位置
我是win10 + arch 双系统,并且efi分区用的是win10自动创建的(大小100m),所以这些空间很快就不够用了(内核和initramfs都放在了ESP分区当中) 我原本是直接把win的ef ...
- 第十届蓝桥杯省赛-试题E: RSA 解密
试题E: RSA 解密 这里涉及到很多数论的知识:质因子分解,扩展欧几里得算法,快速幂算法,利用快速乘算法求解快速幂(mod太大导致不能直接乘,而是需要使用加法来替代乘法) 另外还需要注意扩展欧几里得 ...
- Java的特性和优势以及不同版本的分类,jdk,jre,jvm的联系与区别,javadoc的生成
Java 1.Java的特性和优势 Write Once,Run Anywhere 简单性 面向对象 可移植性 高性能 分布式 动态性 多线程 安全性 健壮性 2.Java的三大版本 JavaSE:标 ...
- xss和实体编码的一点小思考
首先,浏览器渲染分以下几步: 解析HTML生成DOM树. 解析CSS生成CSSOM规则树. 将DOM树与CSSOM规则树合并在一起生成渲染树. 遍历渲染树开始布局,计算每个节点的位置大小信息. 将渲染 ...
- 事件 on
$(选择器).on(事件名称,事件的处理函数) 事件名称:js事件去掉on的部分,例如js中onclick,这里就是click 例如:<input type="button" ...
- Elasticsearch 结构化搜索、keyword、Term查询
前言 Elasticsearch 中的结构化搜索,即面向数值.日期.时间.布尔等类型数据的搜索,这些数据类型格式精确,通常使用基于词项的term精确匹配或者prefix前缀匹配.本文还将新版本的&qu ...