本文为 Django 学习笔记的讲解。
所有的代码见【Django】系列。
视图的作用是接收并响应 web 请求,其本质是一个 python 中的函数。视图可以对网页和 JSON 数据进行响应,其中网页包括重定向和错误视图(如 404,500, 400);JSON 数据是网页中发出 Ajax 请求的问题。
其响应过程如图:
首先制定根级 url 配置文件,在 settings,py 中的 ROOT_URLCONF = 'project.urls'
,一般默认实现无需修改。
urlpatterns 是一个 url 实例的列表,里面存 url 对象,实例化时传三个参数,分别为:正则表达式,视图名称,名称(使用反向解析,后面会讲到),include()
表示从下个文件中找视图名称。
urlpatterns = [
re_path(r'^admin/', admin.site.urls),
re_path(r'^', include('myApp.urls')),
]
如果想从 url 中获取一个值,则需要对正则加小括号,例如在 Django 模板基本操作中,我们通过 (\d+)
获得班级序号。我们通常会在匹配正则最后加反斜杠,则匹配正则前无需重复加反斜杠。正则前加 r
表示字符串不进行转义。
我们一般在应用中创建 urls.py 文件,定义本应用的 url 配置。因为整个工程不止一个项目,如果全写在 project 下的 urls.py 中,则不方便应用的管理。因此我们需要在 project/urls.py 中使用 include
方法:
from django.contrib import admin
from django.urls import re_path, include
urlpatterns = [
re_path(r'^admin/', admin.site.urls),
re_path(r'^', include('myApp.urls', namespace="myApp")), # 反向代理
]
from django.urls import re_path
from . import views
# app_name = 'myApp' # 要加app_name才能使用反向代理
urlpatterns = [
re_path(r'^$', views.index, name="index"), # 反向代理
]
匹配过程见 Django 模板基本操作。
如果在视图、模板中使用了硬编码的链接。如点击首页的 a,则生成 url 跳转到下一个页面:
<body>
<h1>Mike is a good manh1>
<a href="first/second">aa>
body>
下个页面路径,如果我们匹配了路径 first/second 则会响应新的页面:
但当我们修改了 project/urls.py 中的 url 时,我们需要重新写应用中的所有 url 路径,工作量极大。我们可以使用方向解析/代理来解决这个问题,上面代码中的 namespace
的作用就是在 url 配置改变时,动态生成链接的地址。使用 url 模板时经常使用。
视图的本质在 Django 视图基本操作中已经讲过,就是一个函数。函数的第一个参数是一个 HttpRequest 的实例,是由浏览器发送过来的,浏览器发来的信息全部存在里面,通常写为 request
(形参想叫什么都可以)。后面的参数为正则表达式获取的参数。一般将视图放在 views.py 文件下定义。
找不到网页(即 url 匹配不成功)时返回。404 视图是相对于整个工程的视图,因此需要在 templates 下定义 404.html,且文件名必须为 404。无需配置 url。
<head>
<meta charset="UTF-8">
<title>404页面title>
head>
<body>
<h1>页面丢失h1>
<h2>{{request_path}}h2>
body>
想要展示 404 页面,我们还需要配置 settings.py 文件:
DEBUG = False # 如果为True则永远无法调用404
ALLOWED_HOSTS = ['*'] # 允许访问,'*'为任何人可访问
得到结果:
在视图代码中出现错误(服务器代码出错)
错误出现在客户操作,如网络爬虫或用户操作修改 cookies。
服务器接收 http 请求后,Django 会根据报文创建 HttpRequest 对象,在之后调用视图时传递给视图,视图的第一个参数就是 HttpRequest 对象。
以下属性都是字符串形式
path
:请求的完整路径,不包括域名和端口
method
:请求的方式,常用的有 GET、POST
encoding
:表示浏览器提交数据的编码方式,可设置,一般为 utf-8。例如使用 google chrome 打开 www.baidu.com 并按 f12,点击 Network 找到 www.baidu.com 中的 Request Header,就可以找到 Accept-Encoding 字段:
GET
:类似字典的对象,包含了 get 请求的所有参数:
POST
:类似字典的对象,包含了 post 请求的所有参数。
FILES
:类似字典的对象,包含了所有上传的文件(文件数据)。
COOKIES
:字典,包含所有的 cookie
session
:类似字典的对象,表示当前会话
我们可以打印查看以上的这些值:
def attribles(request):
print(request.path)
print(request.method)
print(request.encoding)
print(request.GET)
print(request.POST)
print(request.FILES)
print(request.COOKIES)
print(request.session)
return HttpResponse("attribles")
得到打印结果:
is_ajax()
:如果是 XMLHttpRequest 发起的,返回 True。一般返回 JSON 数据
request 对象中的 GEt、POST 都属于 QueryDict 对象。方法有:
get()
:根据键获取值。如 www.sunck.wang/abc?a=1&b=2&c=3 获取 a 的值。getlist()
:将键的值以列表形式返回。如 www.sunck.wang/abc?a=1&a=2&c=3 获取 a 的值GET 用来获取浏览器传递过来的数据,这里我们使用两种方式获取 GET 属性。首先输入 http://127.0.0.1:8000/sunck/get1?a=1&b=2&c=3:
# 获取get传递的数据
def get1(request):
a = request.GET.get('a')
b = request.GET['b'] # 也可以这样获取
c = request.GET.get('c')
return HttpResponse(a + " " + b + " " + c)
得到结果:
但此时输入 http://127.0.0.1:8000/sunck/get1?a=1&a=2&c=3 提示服务器内部出错,无法获取两个 a:
第二种输入 http://127.0.0.1:8000/sunck/get1?a=1&a=2&c=3:
def get2(request):
a = request.GET.getlist('a')
a1 = a[0]
a2 = a[1]
c = request.GET.get('c')
return HttpResponse(a1 + " " + a2 + " " + c)
得到结果:
这里使用表单提交实现 post 请求。
首先在 templates/myApp 下创建 register.html 文件,添加表单样式:
<body>
<form action="register/" method="post">
姓名:<input type="text" name="name" value=""/>
<hr/>
性别:<input type="radio" name="gender" value="1"/>男
<input type="radio" name="gender" value="0"/>女
<hr/>
年龄:<input type="text" name="age" value=""/>
<hr/>
爱好:<input type="checkbox" name="hobby" value="code"/>编程
<input type="checkbox" name="hobby" value="math"/>数学
<hr/>
<input type="submit" value="注册"/>
form>
body>
然后配置 url:
urlpatterns = [
re_path(r'^$', views.index, name="index"),
re_path(r'^attribles/$', views.attribles),
re_path(r'^get1/$', views.get1),
re_path(r'^get2/$', views.get2),
re_path(r'^showregister/$', views.showregister),
re_path(r'^showregister/register/$', views.register),
]
定义视图:
def showregister(request):
return render(request, 'myApp/register.html')
def register(request):
name = request.POST.get("name")
gender = request.POST.get("gender")
age = request.POST.get("age")
hobby = request.POST.getlist("hobby")
print(name)
print(gender)
print(age)
print(hobby)
return HttpResponse("post")
此时点击注册会显示 CSRF 禁止:
我们还需要在 settings.py 中注释这一行代码,关闭 csrf:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware', # 关闭csrf
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
输入表单:
打印得到结果为:
HttpResponse 是给浏览器返回数据的。前面介绍了 HttpRequest 对象是由 django 创建的,而
HttpResponse 则是程序员创建的。
返回数据有两种用法,第一种是不调用模板,直接返回数据:
from django.http import HttpResponse
def index(request):
return HttpResponse("Mike is a good man")
第二种是调用模板,通常使用 render 方法。render 的原型为 render(request, templateName, [context])
,其中 request
为请求体对象,templateName
为模板路径, context
为传递给需要渲染在模板上的数据,可缺省。我们可以结合数据和模板,返回完整的 HTML 页面。
def index(request):
return render(request, 'myApp/index.html')
我们查看 www.baidu.com 的 Response Headers:
charset
:数据的编码格式status_code
:响应状态码,有 200,304,404。content-type
:指定输出的 MIME 类型(HTML 或 JSON)我们在这里打印上面的属性:
def showresponse(request):
res = HttpResponse() # 自己创建对象
res.content = b'good'
print(res.content)
print(res.charset)
print(res.status_code)
print(res.content-type)
return res
得到打印结果:
init
:使用页面内容实例化 HttpResponse 对象write(content)
:以文件的形式写入flush()
:以文件的形式输出缓冲取set_cookie(key, value='', max_age=None, expires=None)
:设置 cookiedelete_cookie(key)
:删除 cookie,若不存在则不进行操作。关于什么是 cookie,我们用一张图来解释它:
首先浏览器登录,提供账号密码给服务器。服务器去数据库验证账号密码(此处省略 Redis 的过程)并得到验证结果。服务器生成 Token 存入数据库对应用户下,并交给浏览器。浏览器的后续验证将使用 Token(实际是一个字符串) 而不是 cookie,因为 cookie 很容易被截获且长度有限。如果 Token 被截获则不会泄露账号密码。
例如输入 www.baidu.com 得到的 cookie 为,但我们不能使用:
我们设置一个 cookie:
def cookietest(request):
res = HttpResponse()
cookie = res.set_cookie("sunck", "good") # 设置cookie
return res
查看页面对应的 cookie:
Headers 中为:
这里的 cookie 我们还可以通过属性来获取:
def cookietest(request):
res = HttpResponse()
cookie = request.COOKIES
res.write(""
+ cookie["sunck"]+"") # 获取sunck对应的cookie并展示
return res
得到结果:
以后发送 http 请求时都会携带 cookie,只验证 cookie 里面的 token 值。
该子类用来实现重定向,完成服务器端的跳转。例如我们输入 www.baidu.com 和 www.baidu.com/index.html 的效果是一样的,这就是重定向的效果。我们希望输入 127.0.0.1:8000/sunck/redirect1 跳转到页面 2:
from django.http import HttpResponseRedirect
def redirect1(request):
return HttpResponseRedirect('/sunck/redirect2')
def redirect2(request):
return HttpResponse("重定向")
实现重定向:
我们再举一个例子,实现与百度类似的功能:
首先配置 url:
urlpatterns = [
re_path(r'^$', views.index, name="index"),
re_path(r'index.html', views.index1), # 输入index.html进入index1视图
]
定义视图:
def index1(request): # 重定向到空路径,匹配url后进入index视图
return HttpResponseRedirect('/sunck')
def index(request):
return render(request, 'myApp/index.html')
输入 127.0.0.1:8000/sunck/index.html,得到结果与主页相同:
这样做可以简化代码,大量的操作只需要写一份即可。我们也可以利用缩写简化代码,得到同样的结果:
from django.shortcuts import redirect
def redirect1(request):
# return HttpResponseRedirect('/sunck/redirect2')
return redirect('/sunck/redirect2')
to 推荐使用反向解析。
用来返回 json 数据,一般用于异步请求,如 ajax。如果 is_ajax()
为真,则返回 json 数据。__init__(self, data)
的 data
为 返回的 json 数据,是字典对象。大致过程为:
from django.http import JsonResponse
def redirect2(request):
if request.is_ajax()
a = JsonResponse() # 创建JsonResponse对象,参数中传json数据
return a
其对象的 content-type 为 application/json
http 协议是无状态的,每次请求都是一次新的请求,不记得之前的请求。例如我们登录某一网站的主页,右上角会显示未登录,如果仅为 http 请求,这次登录后我们下一次进入网页还是会显示未登录。
客户端和服务器的一次通信就是一次会话。因此如果我们想进行状态保持,需要在客户端或服务端存储有关会话的数据。在客户端和服务端通常分别用 cookie 和 session。使用 cookie,所有的数据存储在客户端,不要存敏感数据;使用session,所有的数据存储在服务端,在客户端用 cookie 存储 session_id,也是一种 key-value 类型。
状态保持在一段时间内跟踪请求者的状态,可跨页面访问当前请求者的数据。例如会员和普通用户在商城中商品显示的价格不同。不同请求者间不会共享此数据,与请求者一一对应。
工程会在 settings.py 文件中默认启用 session。
INSTALLED_APPS = [
'django.contrib.sessions',
]
MIDDLEWARE = [
'django.contrib.sessions.middleware.SessionMiddleware',
]
启用 session 后,每个 HttpResquest 对象都有一个 session 属性,类似字典的对象。
get(key, default=None)
:根据键获取 session值clear()
:清空所有会话flush()
:删除当前会话并删除会话的 cookie在这里我们实现一个登录界面,实现首页面显示游客,点击登录跳转到登陆页面,输入用户名后返回主界面,显示用户名。首先渲染首页面和登录页面:
<body>
<h1>欢迎:游客h1>
<a href="/sunck/login">登录a>
body>
<body>
<form action="/sunck/showmain" method="post">
<input type="text" name="username"/>
<input type="submit" value="登录">
form>
body>
视图定义如下:
def main(request):
return render(request, 'myApp/main.html')
def login(request):
return render(request, 'myApp/login.html')
def showmain(request): # 重定向到main
return redirect('/sunck/main')
配置路径:
re_path(r'^main/$', views.main),
re_path(r'^login/$', views.login),
re_path(r'^showmain/$', views.showmain),
此时跳转回 main 时还不能获得用户名,我们还需要获取 session。在 showmain 视图定义中存储 session,在 main 中获取 session 得到 username 传给模板进行显示。
def main(request):
# 取session
username = request.session.get('name', "游客")
return render(request, 'myApp/main.html', {'username': username})
def showmain(request):
username = request.POST.get('username')
# 存session
request.session['name'] = username
return redirect('/sunck/main')
模板渲染:
<body>
<h1>欢迎:{{username}}h1>
<a href="/sunck/login">登录a>
body>
得到主界面:
点击登录:
返回主界面:
注意:这里容易出 bug,在反复尝试前要清空浏览器的缓存,否则主界面可能会出现显示 None 的情况。
此时我们可以查看数据库中的 session(第条个数据出现了 bug,请忽视),第一列为 session_key ,第二列为 session_data,第三列为 expire_data,即过期时间:
在线 base64 解码:
将数据库中的 session_key 字段与浏览器中的 sessionid 比较发现相同:
在登录后,我们还需要清除 session,清除 session 有很多种方法,下面我们通过代码一一介绍。
先修改模板:
<body>
<form action="/sunck/showmain/" method="post">
<input type="text" name="username"/>
<input type="submit" value="登录">
form>
body>
再定义视图
from django.contrib.auth import logout
def quit(request):
# 清除session,三种方法均可,推荐使用logout
logout(request)
# request.session.clear()
# request.session.flush()
return redirect('/sunck/main')
点击退出,用户名显示为游客:
一般网页还有设置 session 过期时间的功能。如果不设置,则默认两星期后过期。
def showmain(request):
username = request.POST.get('username')
# 存session
request.session['name'] = username
request.session.set_expiry(10) # 设置整数,10s后过期
return redirect('/sunck/main')
此时数据库中的过期时间为:
还可以设置为时间对象,到达该时间则过期;设置为 0,关闭浏览器时失效;None 永不过期,一般不使用。
session 存储的位置有以下三种:
SESSION_ENGINE = "django.contrib.sessions.backends.db"
。SESSION_ENGINE = "django.contrib.sessions.backends.cache"
。SESSION_ENGINE = "django.contrib.sessions.backends.cached_db"
。在 cmd 中使用命令 pip install django-redis-sessions
,并在 settings.py 中添加:
SESSION_ENGINE = 'redis_sessions.session'
SESSION_REDIS_HOST = 'localhost'
SESSION_REDIS_PORT = 6379 # 默认端口号6379
SESSION_REDIS_DB = 0
SESSION_REDIS_PASSWORD = 'sunck' # 账号
SESSION_REDIS_PREFIX = session
我们的视图部分到这里就结束啦,相信你已经学会文章中有关视图的所有东西了。有问题欢迎与我交流