python django基础三 模版渲染
request对象
当一个页面被请求时,Django就会创建一个包含本次请求原信息的HttpRequest对象。
Django会将这个对象自动传递给响应的视图函数,一般视图函数约定俗成地使用 request 参数承接这个对象。
print(request.method) # 请求方式 print(request.path) # 请求路径 print(request.GET) # GET请求数据,返回QueryDict对象 print(request.POST) # POST请求的数据,返回QueryDict对象 print(request.get_full_path()) # 包含GET请求参数的请求路径 print(request.is_ajax()) #判断是否为ajax请求,返回布尔值 return HttpResponse("OK")
HttpResponse对象
响应对象主要有三种形式:
- HttpResponse()
- render()
- redirect()
HttpResponse()括号内直接跟一个具体的字符串作为响应体,比较直接很简单
render(request, template_name,[context])
- request: 用于生成响应的请求对象。
- template_name:要使用的模板的完整名称,可选的参数
- context:添加到模板上下文的一个字典。默认是一个空字典。如果字典中的某个值是可调用的,视图将在渲染模板之前调用它。
render方法就是将一个模板页面中的模板语法进行渲染,最终渲染成一个html页面作为响应体。
修改views.py,使用render渲染index.html。传一个参数name
def index(request):
'''
request: 所有请求信息的封装对象
HttpResponse: 最终return的一定是HttpResponse的实例对象
:param request:
:return:
'''
print(request.method) # 请求方式
print(request.path) # 请求路径
print(request.GET) # GET请求数据,返回QueryDict对象
print(request.POST) # POST请求的数据,返回QueryDict对象
print(request.get_full_path()) # 包含GET请求参数的请求路径
print(request.is_ajax()) #判断是否为ajax请求,返回布尔值
# return HttpResponse("OK")
name = 'xiao'
return render(request,"index.html",{"name":name})
在templates目录下,创建index.html文件,内容如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h3>INDEX</h3> <p>Hi, {{ name }}</p> </body> </html>
重新访问index页面
redirect() 传递要重定向的一个硬编码的URL
def my_view(request):
...
return redirect('/some/url/')
也可以是一个完整的URL:
def my_view(request):
...
return redirect('http://example.com/')
修改views.py
先导入redirect类
from django.shortcuts import render,HttpResponse,redirect
def login(request):
if request.method == "POST":
user = request.POST.get("user")
pwd = request.POST.get('pwd')
if user == 'xiao' and pwd == '123':
#return HttpResponse("登录成功") # 它并不会跳转,需要用户自己手动跳转,体验不好!
return redirect('/index/') # 重定向到index
return render(request,'login.html')
修改mysite下面的settings.py,关闭csrf
重新访问login页面,这个时候,打开浏览器控制台-->network。点击提交按钮
页面跳转至首页
查看控制台,注意:它用了2次连接。一次是302,一次是200
查看login的响应体,发现它是空的
index是有响应体的
关于重定向,简单来说:
通过修改http协议的header部分,对浏览器下达重定向指令的,让浏览器对location中指定的url提出请求,使浏览器显示重定向网页的内容。
301和302的区别。
301和302状态码都表示重定向,就是说浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的URL地址,这个地址可以从响应的Location首部中获取
(用户看到的效果就是他输入的地址A瞬间变成了另一个地址B)——这是它们的共同点。
他们的不同在于。301表示旧地址A的资源已经被永久地移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址;
302表示旧地址A的资源还在(仍然可以访问),这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址。 SEO302好于301
重定向原因
(1)网站调整(如改变网页目录结构);
(2)网页被移到一个新地址;
(3)网页扩展名改变(如应用需要把.php改成.Html或.shtml)。
推荐传参写法
views.py
def timer(request):
import time
ctime = str(time.time())
return render(request,"timer.html",{'now':ctime})
在templates目录下创建timer.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
现在时刻是:<h1>{{ now }}</h1>
</body>
</html>
模板语法之变量
views.py
def index(request):
import datetime
s = 'hello'
l = [111, 222, 333] # 列表
dic = {'name': 'yuan', 'age': 18} # 字典
date = datetime.date(1993, 5, 2) # 日期对象
class Person():
def __init__(self, name):
self.name = name
person_yuan = Person("yuan") # 自定义类对象
person_egon = Person("egon")
person_alex = Person("alex")
person_list = [person_yuan, person_egon, person_alex]
return render(request, 'index.html', {'l': l, 'dic': dic, 'date': date, 'person_list': person_list})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h4>{{s}}</h4>
{#去l第一个#}
<h4>列表:{{ l.0 }}</h4>
<h4>列表:{{ l.2 }}</h4>
<h4>字典:{{ dic.name }}</h4>
<h4>字典:{{ dic.name.upper }}</h4>
<h4>日期:{{ date.year }}</h4>
<h4>类对象列表:{{ person_list.0.name }}</h4>
</body>
</html>
深度查询
列表
def index(request):
li = [123, 456, 567]
return render(request,"index.html",{"li":li})
修改index.html,修改body部分
<p>{{ li }}</p>
取第一个元素
<p>{{ li.0 }}</p>
取第二个元素
<p>{{ li.1 }}</p>
取第三个元素
<p>{{ li.2 }}</p>
字典
def index(request):
info = {'name':'xiao','age':'23'}
return render(request,"index.html",{"info":info})
修改index.html,修改body部分
<p>{{ info }}</p>
取name
<p>{{ info.name }}</p>
取age
<p>{{ info.age }}</p>
类
def index(request):
class Annimal(object):
def __init__(self, name, age):
self.name = name
self.age = age
def call(self):
return "汪汪汪"
dog = Annimal("旺财", 3)
cat = Annimal("小雪", 4)
duck = Annimal("小黄", 5)
animal_list = [dog, cat, duck]
return render(request,"index.html",{"animal_list":animal_list})
修改index.html,修改body部分
<p>{{ animal_list }}</p>
取一个对象:
<p>{{ animal_list.0 }}</p>
取属性:
<p>{{ animal_list.0.name }}</p>
取方法:
<p>{{ animal_list.0.call }}</p>
注意:取方法的时候,不要加括号。它不能接收参数!
call方法,必须用return。否则取方法时,结果输出None
总结:深度查询,就是不停的点点点就行了。
模板之过滤器
语法:
{{obj|filter__name:param}}
add
修改index视图函数
def index(request):
num = 50
return render(request,"index.html",{"num":num})
修改index.html,修改body部分
<p>{{ num|add:100 }}</p>
访问网页,输出:
default
如果一个变量是false或者为空,使用给定的默认值。否则,使用变量的值。例如:
{{ value|default:"nothing" }}
修改index视图函数
def index(request):
book_list = []
return render(request,"index.html",{"book_list":book_list})
修改index.html,修改body部分
<p>{{ book_list|default:'没有任何书籍' }}</p>
访问网页,输出:
length
返回值的长度。它对字符串和列表都起作用。例如:
{{ value|length }}
举例:
修改index视图函数
def index(request):
female_star = ['赵丽颖','宋茜','霍思燕','唐嫣']
return render(request,"index.html",{"female_star":female_star})
修改index.html,修改body部分
<p>{{ female_star|length }}</p>
访问网页,输出:
filesizeformat
将值格式化为一个 “人类可读的” 文件尺寸 (例如 '13 KB'
, '4.1 MB'
, '102 bytes'
, 等等)。例如:
{{ value|filesizeformat }}
举例:
修改index视图函数
def index(request):
file_size = 123456789
return render(request,"index.html",{"file_size":file_size})
修改index.html,修改body部分
<p>{{ file_size|filesizeformat }}</p>
访问网页,输出:
date
如果 value=datetime.datetime.now()
{{ value|date:"Y-m-d" }}
举例:
修改index视图函数
def index(request):
import datetime
now = datetime.datetime.now()
return render(request,"index.html",{"now":now})
修改index.html,修改body部分
<p>{{ now|date:'Y-m-d H:i:s' }}</p>
访问网页,输出:
slice
如果 value="hello world"
{{ value|slice:"2:-1" }}
举例:
修改index视图函数
def index(request):
val = 'hello world'
return render(request,"index.html",{"val":val})
修改index.html,修改body部分
<p>{{ val|slice:'1:-1' }}</p>
访问网页,输出:
注意:它和python切片方法是一样的,顾头不顾尾巴。
truncatechars
参数:要截断的字符数
例如:
{{ value|truncatechars:9 }}
举例:
比如后台管理页面,展示文章内容部分,一般取前20个字符串,后面直接用...来代替。
修改index视图函数
def index(request): text = 'Django 教程 Python下有许多款不同的 Web 框架。Django是重量级选手中最有代表性的一位。许多成功的网站和APP都基于Django。' return render(request,"index.html",{"text":text})
修改index.html,修改body部分
<p>{{ text|truncatechars:20 }}</p>
访问网页,输出:
with
使用一个简单地名字缓存一个复杂的变量,多用于给一个复杂的变量起别名
举例:
修改index视图函数
def index(request): class Annimal(object): def __init__(self, name, age): self.name = name self.age = age def call(self): return "汪汪汪" dog = Annimal("旺财", 3) cat = Annimal("小雪", 4) duck = Annimal("小黄", 5) animal_list = [dog, cat, duck] return render(request, "index.html", {"animal_list": animal_list})
修改index.html,修改body部分
<div> {% with f_name=animal_list.1.name %} <p>{{ f_name }}</p> {% endwith %} </div>
访问网页,输出:
join
使用字符串连接列表,{{ list|join:', ' }},就像Python的str.join(list)
模板之标签
for标签
{% for person in person_list %}
<p>{{ person.name }}</p>
{% endfor %}
举例:遍历列表
修改index视图函数
def index(request):
li = [123, 456, 567]
return render(request, "index.html", {"li": li})
修改index.html,修改body部分
<p>
{% for item in li %}
<span>{{ item }}</span>
{% endfor %}
</p>
访问网页,输出:
遍历一个字典:
{% for key,val in dic.items %}
<p>{{ key }}:{{ val }}</p>
{% endfor %}
forloop.counter 从1开始计数
forloop.counter0 从0开始基数
forloop.revcounter 倒序到1结束
forloop.revcounter0 倒序到0结束
forloop.first 是一个布尔值,如果该迭代是第一次执行,那么它被置为True
forloop.last 是一个布尔值,如果该迭代是最后一次执行,那么它被置为True
举例:
修改index视图函数
def index(request): female_star = {'name':'赵丽颖', 'age':'25', 'job':'movie', 'place':'Beijing'} return render(request, "index.html", {"female_star": female_star})
修改index.html,修改body部分
<p> {% for key,val in female_star.items %} <span>序号:{{ forloop.counter }} {{ key }}:{{ val }}</span><br/> {% endfor %} </p>
访问网页,输出:
for ... empty
for 标签带有一个可选的{% empty %} 从句,以便在给出的组是空的或者没有被找到时,可以有所操作。
举例:
修改index视图函数
def index(request): person_list = [] return render(request, "index.html", {"person_list": person_list})
修改index.html,修改body部分
<div> {% for person in person_list %} <p>{{ person.name }}</p> {% empty %} <p>对不起,这里没有人</p> {% endfor %} </div>
访问网页,输出:
因为列表为空,所以输出没有人。
if 标签
{% if num > 100 or num < 0 %}
<p>无效</p>
{% elif num > 80 and num < 100 %}
<p>优秀</p>
{% else %}
<p>凑活吧</p>
{% endif %}
举例:
博客园右上角部分,未登录时,显示登录和注册。登录之后,显示注销和修改密码。
修改index视图函数
def index(request): login_user = 'xiao' return render(request, "index.html", {"login_user": login_user})
修改index.html,修改body部分
<div> {% if login_user %} <p>{{ login_user.name }}</p> <a href="">注销</a> <a href="">修改密码</a> {% else %} <a href="">登陆</a> <a href="">注册</a> {% endif %} </div>
访问网页,输出:
csrf_token
这个标签用于跨站请求伪造保护
编辑settings.py,查看MIDDLEWARE配置,确保CsrfViewMiddleware这一行内容没有被注释掉。默认是开启的
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
修改index.html,修改body部分。
action为空,表示提交至当前页面
<form action="" method="post"> <lable>用户名:</lable> <input type="text" name="user"> <input type="submit"> </form>
访问网页,输出:
输出内容,点击提交
页面提示被csrf拒绝了
增加csrf_token,就可以通过了。
修改index.html,修改body部分。
注意:csrf_token必须在form表单里面!!!
<form action="" method="post">
{% csrf_token %}
<lable>用户名:</lable>
<input type="text" name="user">
<input type="submit">
</form>
再次提交,就不会被拒绝了。
使用浏览器控制台,查看html代码
可以看到,它加了input框,是django帮你加的。
注意:它的属性值是hidden,表示隐藏。
它的name值是csrfmiddlewaretoken,那么点击提交时。它会提交给django服务器。
查看Headers,发送了post数据
django接收到这个数据时,就会认为它是安全的。
模板继承 (extend)
base.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">
<!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
<title>
{% block title %}
母板的title信息
{% endblock %}
</title>
<style>
body{
padding: 0;
margin: 0;
}
.nav{
background-color: red;
height: 40px;
}
.left_list div{
width: 100px;
height: 20px;
background-color: pink;
border-bottom:1px solid black ;
}
.left_list{
float: left;
width: 100px;
}
.content{
float: right;
width: 600px;
height: 100px;
}
</style>
</head>
<body>
<div class="nav">
<a href="">新闻</a>
<a href="">问也</a>
<a href="">图片</a>
</div>
<div class="left_list">
{% block list %}
<div><a href="/index/">首页</a></div>
<div><a href="/order/">订单页</a></div>
<div><a href="/user/">个人中心</a></div>
{% endblock %}
</div>
<div class="content">
{% block content %}
母板预留的地方
{% endblock %}
</div>
</body>
</html>
index.html
{% extends 'base.html' %}
{% block title %}
{# 首页#}
{% endblock %}
{% block content %}
<a href="">首页</a>
<span>首页介绍</span>
{{ block.super }}
{% endblock %}
order.html
{% extends 'base.html' %}
{% block title %}
订单
{% endblock %}
{% block content %}
订单页
{% endblock %}
usercenter.html
{% extends 'base.html' %}
{% block title %}
个人中心
{% endblock %}
{% block content %}
个人中心页
{% endblock %}
组件
可以将常用的页面内容如导航条,页尾信息等组件保存在单独的文件中,然后在需要使用的地方,文件的任意位置按如下语法导入即可。
{% include 'navbar.html' %}
例如:有个如下的导航栏,nav.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .c1{ background-color: red; height: 40px; } </style> </head> <body> <div class="c1"> <div> <a href="">xx</a> <a href="">dd</a> </div> </div> </body> </html>
嵌入导航栏的页面,test.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {% include 'nav.html' %} <h1>xxxxxxxxxx</h1> </body> </html>
cbv和FBV
FBV(function base views) 就是在视图里使用函数处理请求。
CBV(class base views) 就是在视图里使用类处理请求
Python是一个面向对象的编程语言,如果只用函数来开发,有很多面向对象的优点就错失了(继承、封装、多态)。所以Django在后来加入了Class-Based-View。可以让我们用类写View。这样做的优点主要下面两种:
- 提高了代码的复用性,可以使用面向对象的技术,比如Mixin(多继承)
- 可以用不同的函数针对不同的HTTP方法处理,而不是通过很多if判断,提高代码可读性
如果我们要写一个处理GET方法的view,用函数写的话是下面这样。
from django.http import HttpResponse def my_view(request): if request.method == 'GET': return HttpResponse('OK')
如果用class-based view写的话,就是下面这样
from django.http import HttpResponse from django.views import View class Myd(View): name = 'sb' def get(self,request,n): print('get方法执行了') print('>>>',n) return render(request,'cvpost.html',{'name':self.name}) def post(self,request,n): print('post方法被执行了') return HttpResponse('post')
注意:使用CBV时,urls.py中也做对应的修改:
# urls.py from django.conf.urls import url from myapp.views import MyView #引入我们在views.py里面创建的类 urlpatterns = [ url(r'^index/$', MyView.as_view()), ]
添加类的属性可以通过两种方法设置,第一种是常见的Python的方法,可以被子类覆盖。
from django.http import HttpResponse from django.views import View class GreetingView(View): name = "yuan" def get(self, request): return HttpResponse(self.name) # You can override that in a subclass class MorningGreetingView(GreetingView): name= "alex"
第二种方法,你也可以在url中指定类的属性:
在url中设置类的属性Python
urlpatterns = [ url(r'^index/$', GreetingView.as_view(name="egon")), #类里面必须有name属性,并且会被传进来的这个属性值给覆盖掉 ]
模板自定义标签和过滤器
标签,是为了做一些功能。过滤器,是对斜杠前面的数据做过滤。
为什么要自定义标签和过滤器?因为自带的不够用,需要结合需求,来自定义。
1、在settings中的INSTALLED_APPS配置当前app,不然django无法找到自定义的simple_tag. 2、在app中创建templatetags模块(模块名只能是templatetags) 3、创建任意 .py 文件,如:my_tags.py
自定义过滤器
举例:增加一个乘法过滤器
修改settings.py中的INSTALLED_APPS,最后一行添加当前的app。
django开头的,都是一些自带的app。它内置在django源码里面!
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01.apps.App01Config', ]
在app01目录里面新建一个templatetags目录,目录名必须是这个!!!否则django找不到。
目录里面创建my_filter_tag.py,这个py文件名,可以随便定义。内容如下:
from django import template from django.utils.safestring import mark_safe register=template.Library() @register.filter def multi_filter(x,y): return x*y
注意:头部的3行,是固定写法,不能改变
增加@register.filter,是为了将函数转换成过滤器。函数的名字,可以自定义。
修改views.py里面的index函数,内容如下:
def index(request): num = 100 return render(request,'index.html',{'num':num})
修改index.html,修改body部分
注意:在使用自定义标签和过滤器,必须在html文件中,导入之前创建的my_filter_tag
{% load my_filter_tag %} <p>{{ num|multi_filter:5 }}</p>
load表示导入模块。p标签中的内容,是执行了multi_filter过滤器。
注意:它接收了2个参数。一个是num,一个是5。因为multi_filter过滤器,定义了2个形参,使用它必须传2个参数才行
再次访问index,页面输出:
自定义标签
标签,是为了做一些功能
举例:4个参数的乘法运算
修改my_filter_tag.py,增加multi_tag函数
@register.simple_tag def multi_tag(x,y,z): return x*y*z
@register.simple_tag表示将函数转换为自定义标签
修改index.html,修改body部分
注意:调用标签,使用{% 标签过滤器名 参数1,参数2,参数3... %}
参数不限,但不能放在if for语句中
{% load my_filter_tag %}
<p>{% multi_tag 7 8 9 %}</p>
重启django项目,访问网页,输出:
###########################代码演示############
创建 templatetags
my_filter_tag.py文件
from django import template from django.utils.safestring import mark_safe register=template.Library() ###自定义过滤器 @register.filter def multi_filter(x,y): return x*y ###自定义标签 @register.simple_tag#表示将函数转换为自定义标签 def my_input(id,arg): result = "<input type='text' id='%s' class='%s' />" %(id,arg,) return mark_safe(result) ###inclusion_tag 多用于返回html代码片段 @register.inclusion_tag('result.html') def show_results(n): #参数可以传多个进来 n = 1 if n < 1 else int(n) data = ["第{}项".format(i) for i in range(1, n+1)] return {"data": data}#这里可以穿多个值,和render的感觉是一样的{'data1':data1,'data2':data2....}
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {% load my_filter_tag %} <p>{{ num|multi_filter:5 }}</p> <p>{% my_input 2 888 %}</p> {% show_results 10 %} </body> </html>
result.html
<ul> {% for choice in data %} <li>{{ choice }}</li> {% endfor %} </ul>