学到这里,我们应该知道了大多数的页面的大多数的数据都是不断变更的,这些就是动态页面,静态页面现在很少了,动态页面也就是与数据库有交互的页面!我们需要从数据库拿出数据,然后和html合在一起,浏览器渲染并展示出来
!以前我们是前后端代码不分离,夹杂在一起。这样非常的不清晰!因此python提供了模板语法,使前后端分离开来!
将页面的设计和Python的代码分离开会更干净简洁更容易维护。
python的模板:HTML代码+模板语法
# current_datetime.html 代码 (前端)
<html><body>现在时刻是:<h1>{{current_date}}</h1></body></html>
# 视图函数的代码(后端)
def current_time(req):
import datetime
now=datetime.datetime.now()
return render(req, 'current_datetime.html', {'current_date':str(now)[:19]})
# 像上面这样就实现前后端代码分离!而不是夹杂在一起!
# 这样具备更好的可维护性和可阅读性,前后端分开开发,也提高了开发效率!
在 Django 模板中遍历复杂数据结构的关键是句点字符, 语法:
{{var_name}}
views.py:
def index(request): # 定义index视图函数
import datetime
s="hello"
l=[111,222,333] # 列表
dic={"name":"yuan","age":18} # 字典
date = datetime.date(1993, 5, 2) # 日期对象
class Person(object): # 定义类Person
def __init__(self,name,age):
self.name=name;
self.age=age;
def running(self):
str1="%s_%s"% (self.name,self.age)
return str1
person_yuan=Person("yuan","100") # 自定义类对象
person_egon=Person("egon","80")
person_alex=Person("alex","30")
person_list=[person_yuan,person_egon,person_alex]
return render(request,"index.html",{"l":l,"dic":dic,"date":date,"person_list":person_list})
template:
<h4>{{s}}h4>
<h4>列表:{{ l.0 }}h4>
<h4>列表:{{ l.2 }}h4>
<h4>字典:{{ dic.name }}h4>
<h4>日期:{{ date.year }}h4>
<h4>类对象列表:{{ person_list.0.name }}h4>
注意:句点符也可以用来引用对象的方法(无参数方法,不算self,它是自动传入):
<h4>{{ person_list.0.running }}h4>
结果:
<h4>yuan_100<h4>
调用方法,放的是方法的返回值!
语法:
{{obj|filter__name:param}}
如果value的值是false或者为空,使用给定的默认值nothing。否则,使用变量的值。例如:
{{ value|default:"nothing" }}
返回值的长度。它对字符串和列表都起作用。例如:
{{ value|length }}
如果 value 是 [‘a’, ‘b’, ‘c’, ‘d’],那么输出是 4。
将值格式化为一个 “人类可读的” 文件尺寸 (例如 ‘13 KB’, ‘4.1 MB’, ‘102 bytes’, 等等)。例如:
{{ value|filesizeformat }}
如果 value 是 123456789,输出将会是 117.7 MB。
如果 value=datetime.datetime.now(),这里的value是一个对象
{{ value|date:"Y-m-d" }} # 拿到日期对象,会按照年-月-日这样的格式返回日期
如果 value=“hello world”
{{ value|slice:"2:-1" }} => llo worl # 顾头不顾尾
如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“…”)结尾。
参数:要截断的字符数
例如:
{{ value|truncatechars:9 }}
Django的模板中会对HTML标签和JS等语法标签进行自动转义,原因显而易见,这样是为了安全。好多人这里肯定不能理解,为什么?
1. 假如一片文章下面的评论里有个script标签,这个评论会进入博客园的数据库
2. 所有人再看这个文章,带有script标签的评论内容数据肯定放入html,然后浏览器渲染出来
3. 假如语法标签没有转义,那么评论里的script里的代码也会被执行
4. 这样就可以通过script,控制所有看这个文章的用户的浏览器界面!这也是一种xss攻击!
value="">点击"
{{ value|safe }}
标签看起来像是这样的: {% tag %}。标签比变量更加复杂:一些在输出中创建文本,一些通过循环或逻辑来控制流程,一些加载其后的变量将使用到的额外信息到模版中。一些标签需要开始和结束标签 ,语法如下:
{% tag %}
...标签内容...
{% endtag %})
遍历每一个元素:
<ur>
{% for person in person_list %}
<li>{{ person.name }}</li>
{% endfor %}
</ur>
遍历一个字典:
{% for key,val in dic.items %}
<p>{{ key }}:{{ val }}</p>
{% endfor %}
循环序号可以通过{{ forloop }}显示
forloop.counter 从1开始计数
forloop.counter0 从0开始计数
forloop.revcounter 倒数计数,最后为1
forloop.revcounter0 倒数计数,最后为0
forloop.first 是否是第一次循环,是输出true,不是输出false
forloop.last 是否是最后一次循环,是输出true,不是输出false
使用:
<ur>
{% for person in person_list %}
<li>{{ forloop.counter }} {{ person.name }}</li>
{% endfor %}
</ur>
for … empty从句
for 标签带有一个可选的{% empty %} 从句,以便在给出的组是空的或者没有被找到时,可以执行的操作。
{% for person in person_list %}
<p>{{ person.name }}</p>
{% empty %} # 如果person_list为空,则渲染下面这个p标签
<p>sorry,no person here</p>
{% endfor %}
{% if %}会对一个变量求值,如果它的值是"True"(存在、不为空、且不是boolean类型的false值),对应的内容块会输出。
{% if num > 100 or num < 0 %}
<p>无效</p>
{% elif num > 80 and num < 100 %}
<p>优秀</p>
{% else %}
<p>凑活吧</p>
{% endif %}
使用一个简单地名字缓存一个复杂的变量,当你需要使用一个"昂贵的"方法(比如访问数据库)很多次的时候是非常有用的
例如:
{% with total=business.employees.count %}
{{ total }} employee{{ total|pluralize }}
{% endwith %}
django为用户实现防止跨站请求伪造的功能,通过中间件 django.middleware.csrf.CsrfViewMiddleware 来完成
原理:
当用post提交数据的时候,django会去检查是否有一个csrf的随机字符串,如果没有就会报错,这也是之前在django初识那篇博客注释csrf的原因,错误如下:
csrf_token的生成,让正常访问网站的用户可以post提交数据到服务器!
在form表单里面需要添加{% csrf_token %}
这样当你查看页面源码的时候,可以看到form中有一个input是隐藏的
总结原理:当用户向login页面发送get请求时,会生成一个csrf的随机字符串一起交给用户的浏览器
,并且服务器的cookie中也存放了这个随机字符串,当用户再次提交数据的时候会带着这个随机字符串提交,如果没有这个随机字符串或者随机字符串的值和服务器的cookie中存放的这个随机字符串不一致,则无法提交成功!
示例:
# {% csrf_token %}只用于post请求中!
<form action="/login/" method="post">
{% csrf_token %}
name <input type="text" name="user">
pwd <input type="text" name="pwd">
<input type="submit">
</form>
注意:
1. action="/login/",第一个'/'代表从开始的login url,即为http://127.0.0.1:8000/login/
如果没有第一个'/',则为在当前页面url后拼接login!
2. login后面这个'/',因为urls控制器中login后面就有'/',因此为了匹配上,需要加!
模版继承可以让您创建一个基本的“骨架”模版,它包含站点中的全部元素,并且可以定义能够被子模版覆盖的 blocks 。
下面这个例子很清楚的说明了模板继承
base.html
<html lang="en">
<head>
<meta charset="UTF-8">
{% block title %}
<title>basetitle>
{% endblock %}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<style>
* {
margin: 0;
padding: 0;
}
.header {
width: 100%;
height: 50px;
background-color: #369;
}
style>
head>
<body>
<div class="header">div>
<div class="container">
<div class="row">
<div class="col-md-3">
{% include 'advertise.html' %}
div>
<div class="col-md-9">
{% block con %}
<h4>contenth4>
{% endblock %}
div>
div>
div>
body>
html>
这个模版,我们把它叫作 base.html, 它定义了一个可以用于两列排版页面的简单HTML骨架。“子模版”的工作是用它们的内容填充空的blocks。
order.html
{% extends 'base.html' %}] # 继承母版html
{% block title %} # 覆盖母版的title block
<title>orders</title>
{% endblock %}
{% block con %} # 覆盖母版的con block
{{ block.super }} <!-- 既保留base的,又保留扩写的 -->
<h4>订单</h4>
{% endblock con %} <!-- 可以加名字,增加可读性 -->
优点:像这样可以极大减少多个页面之间相同组件的代码重复情况造成的代码冗余,提高代码的复用性、灵活性,降低代码的耦合度!
需要注意的是: