date: 2022-01-09title: Django之视图函数 #标题
tags: #标签
categories: python # 分类
一个视图函数(类),简称视图,是一个简单的Python 函数(类),它接受Web请求并且返回Web响应。
响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片。
无论视图本身包含什么逻辑,都要返回响应。代码写在哪里也无所谓,只要它在你当前项目目录下面。除此之外没有更多的要求了——可以说“没有什么神奇的地方”。为了将代码放在某处,大家约定成俗将视图放置在项目(project)或应用程序(app)目录中的名为views.py的文件中。
request对象
在说视图函数之前,先来看看视图函数中的request对象,参考官方文档。
当一个页面被请求时,Django就会创建一个包含本次请求原信息(请求报文中的请求行、首部信息、内容主体等)的HttpRequest对象。Django会将这个对象自动传递给响应的视图函数,一般视图函数约定俗成地使用 request 参数承接这个对象。
在之前urls之路由分发章节,我们也写了几个视图函数,下面是一个简单的视图函数(可以看到每个视图函数中,都有一个request形参,就是用来接收HTTPRequest对象的):
from django.shortcuts import render
def index(request):
return render(request, 'app02.html')
def home_page(request):
return render(request, 'app02_home.html')
下面来看看请求相关常用的值:
- path_info:返回用户访问url,不包括域名
- method:请求中使用的HTTP方法的字符串表示,全大写表示。
- GET:包含所有HTTP GET参数的类字典对象
- POST:包含所有HTTP POST参数的类字典对象
- body:请求体,byte类型 request.POST的数据就是从body里面提取到的
注意:所有属性应该都是只读的,除非另有说明。
下面是个简单样例:
1、urls.py文件内容如下:
from django.conf.urls import url
from apps import views
urlpatterns = [
url(r'', views.page),
]
2、views.py文件内容如下:
data列表就是用于存放request请求中的各种数据,至于其含义,可以自行查阅官方文档。
from django.shortcuts import render, HttpResponse
def page(request):
if request.method == 'GET':
return render(request, 'login.html')
else:
data = [
{
'path_info': request.path_info,
'path': request.path,
'method': request.method,
'meta': request.META,
'scheme': request.scheme,
'body': request.body,
'encoding': request.encoding,
'get_data': request.GET,
'post_data': request.POST,
'cookie': request.COOKIES,
'files': request.FILES,
'user': request.user,
'session': request.session
}
]
print(data)
return HttpResponse('登录成功')
3、html文件内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>欢迎来到登录页面</h1>
<form action="/index" method="post">
<label>
用户名:<input type="text" name="username">
</label>
<br/>
<label>
密 码:<input type="password" name="password">
</label>
<br/>
<label>
选择文件:<input type="file" name="file">
</label>
<br/>
<input type="submit">
</form>
</body>
</html>
当访问页面后,按照页面信息进行登录并上传文件,pycharm就会打印类似于下面的数据:
看起来有点乱,我们可以对其进行粘贴,然后去JSON在线解析网站对其进行解析,如下是解析后的内容:
[{
'path_info': '/index',
'path': '/index',
'method': 'POST',
'meta': {
'ALLUSERSPROFILE': 'C:\\ProgramData',
'APPDATA': 'C:\\Users\\Tenyuns\\AppData\\Roaming',
'CLASSPATH': '.;D:\\software\\java\\lib\\dt.jar;D:\\software\\java\\lib\\tools.jar;',
'COMMONPROGRAMFILES': 'C:\\Program Files\\Common Files',
'COMMONPROGRAMFILES(X86)': 'C:\\Program Files (x86)\\Common Files',
'COMMONPROGRAMW6432': 'C:\\Program Files\\Common Files',
'COMPUTERNAME': 'TENYUNS-KLQSALP',
'COMSPEC': 'C:\\Windows\\system32\\cmd.exe',
'DJANGO_SETTINGS_MODULE': 'first_pro.settings',
'DRIVERDATA': 'C:\\Windows\\System32\\Drivers\\DriverData',
'HOMEDRIVE': 'C:',
'HOMEPATH': '\\Users\\Tenyuns',
'IDEA_INITIAL_DIRECTORY': 'C:\\Windows\\System32',
'JAVA_HOME': 'D:\\software\\java',
'JETBRAINS_LICENSE_SERVER': 'http://fls.jetbrains-agent.com',
'LOCALAPPDATA': 'C:\\Users\\Tenyuns\\AppData\\Local',
'LOGONSERVER': '\\\\TENYUNS-KLQSALP',
'NUMBER_OF_PROCESSORS': '8',
'ONEDRIVE': 'C:\\Users\\Tenyuns\\OneDrive',
'ONEDRIVECONSUMER': 'C:\\Users\\Tenyuns\\OneDrive',
'OS': 'Windows_NT',
'PATH': 'D:\\software\\xshell\\Xlpd 6\\;D:\\software\\xshell\\Xshell 6\\;D:\\software\\xshell\\Xmanager 6\\;D:\\software\\python\\Scripts\\;D:\\software\\python\\;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Windows\\System32\\OpenSSH\\;"D:\\software\\java\\bin;D:\\software\\java\\jre\\bin;";D:\\software\\mysql-5.7.30-winx64\\bin;;D:\\software\\WinSCP\\;D:\\software\\PyCharm 2019.3.3\\bin;D:\\software\\Microsoft VS Code\\bin',
'PATHEXT': '.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.PY;.PYW',
'PROCESSOR_ARCHITECTURE': 'AMD64',
'PROCESSOR_IDENTIFIER': 'Intel64 Family 6 Model 142 Stepping 10, GenuineIntel',
'PROCESSOR_LEVEL': '6',
'PROCESSOR_REVISION': '8e0a',
'PROGRAMDATA': 'C:\\ProgramData',
'PROGRAMFILES': 'C:\\Program Files',
'PROGRAMFILES(X86)': 'C:\\Program Files (x86)',
'PROGRAMW6432': 'C:\\Program Files',
'PSMODULEPATH': 'C:\\Program Files\\WindowsPowerShell\\Modules;C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\Modules',
'PUBLIC': 'C:\\Users\\Public',
'PYCHARM': 'D:\\software\\PyCharm 2019.3.3\\bin;',
'PYCHARM_DISPLAY_PORT': '63342',
'PYCHARM_HOSTED': '1',
'PYTHONIOENCODING': 'UTF-8',
'PYTHONPATH': 'D:\\django_demo\\first_pro;D:\\software\\PyCharm 2019.3.3\\plugins\\python\\helpers\\pycharm_matplotlib_backend;D:\\software\\PyCharm 2019.3.3\\plugins\\python\\helpers\\pycharm_display',
'PYTHONUNBUFFERED': '1',
'SYSTEMDRIVE': 'C:',
'SYSTEMROOT': 'C:\\Windows',
'TEMP': 'C:\\Users\\Tenyuns\\AppData\\Local\\Temp',
'TMP': 'C:\\Users\\Tenyuns\\AppData\\Local\\Temp',
'USERDOMAIN': 'TENYUNS-KLQSALP',
'USERDOMAIN_ROAMINGPROFILE': 'TENYUNS-KLQSALP',
'USERNAME': 'Tenyuns',
'USERPROFILE': 'C:\\Users\\Tenyuns',
'WINDIR': 'C:\\Windows',
'WXDRIVE_START_ARGS': '--wxdrive-setting=0 --disable-gpu --disable-software-rasterizer --enable-features=NetworkServiceInProcess',
'__COMPAT_LAYER': 'RunAsAdmin',
'RUN_MAIN': 'true',
'SERVER_NAME': 'activate.navicat.com',
'GATEWAY_INTERFACE': 'CGI/1.1',
'SERVER_PORT': '8000',
'REMOTE_HOST': '',
'CONTENT_LENGTH': '103',
'SCRIPT_NAME': '',
'SERVER_PROTOCOL': 'HTTP/1.1',
'SERVER_SOFTWARE': 'WSGIServer/0.2',
'REQUEST_METHOD': 'POST',
'PATH_INFO': '/index',
'QUERY_STRING': '',
'REMOTE_ADDR': '127.0.0.1',
'CONTENT_TYPE': 'application/x-www-form-urlencoded',
'HTTP_HOST': '127.0.0.1:8000',
'HTTP_CONNECTION': 'keep-alive',
'HTTP_CACHE_CONTROL': 'max-age=0',
'HTTP_SEC_CH_UA': '"Google Chrome";v="95", "Chromium";v="95", ";Not A Brand";v="99"',
'HTTP_SEC_CH_UA_MOBILE': '?0',
'HTTP_SEC_CH_UA_PLATFORM': '"Windows"',
'HTTP_UPGRADE_INSECURE_REQUESTS': '1',
'HTTP_ORIGIN': 'http://127.0.0.1:8000',
'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36',
'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'HTTP_SEC_FETCH_SITE': 'same-origin',
'HTTP_SEC_FETCH_MODE': 'navigate',
'HTTP_SEC_FETCH_USER': '?1',
'HTTP_SEC_FETCH_DEST': 'document',
'HTTP_REFERER': 'http://127.0.0.1:8000/',
'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br',
'HTTP_ACCEPT_LANGUAGE': 'zh-CN,zh;q=0.9,en;q=0.8',
'HTTP_COOKIE': 'csrftoken=TXyBJNxWBWWIQAaf1fwO7NAxnbH9QUDvlGinb2u1vLR9Wo5N85U8Ky1L36XiI4Gr',
'wsgi.input': < _io.BufferedReader name = 884 > ,
'wsgi.errors': < _io.TextIOWrapper name = '<stderr>'
mode = 'w'
encoding = 'utf-8' > ,
'wsgi.version': (1, 0),
'wsgi.run_once': False,
'wsgi.url_scheme': 'http',
'wsgi.multithread': True,
'wsgi.multiprocess': False,
'wsgi.file_wrapper': < class 'wsgiref.util.FileWrapper' >
},
'scheme': 'http',
'body': b 'username=lvjianzhao&password=jianzhao87.&file=Django%E4%B9%8BURL%E8%B7%AF%E7%94%B1%E7%B3%BB%E7%BB%9F.md',
'encoding': None,
'get_data': < QueryDict: {} > ,
'post_data': < QueryDict: {
'username': ['lvjianzhao'],
'password': ['jianzhao87.'],
'file': ['Django之URL路由系统.md']
} > ,
'cookie': {
'csrftoken': 'TXyBJNxWBWWIQAaf1fwO7NAxnbH9QUDvlGinb2u1vLR9Wo5N85U8Ky1L36XiI4Gr'
},
'files': < MultiValueDict: {} > ,
'user': < SimpleLazyObject: < function AuthenticationMiddleware.process_request. < locals > . < lambda > at 0x000001E2F0636280 >> ,
'session': < django.contrib.sessions.backends.db.SessionStore object at 0x000001E2F0687970 >
}]
[09 / Nov / 2021 15: 29: 43]
"POST /index HTTP/1.1"
200 5293
临时重定向
下面是一个简单项目,各文件内容如下:
1、urls.py
from django.conf.urls import url
from apps import views
urlpatterns = [
url(r'^login/', views.login),
]
2、views.py
from django.shortcuts import render, HttpResponse
def login(request):
if request.method == 'GET':
return render(request, 'login.html')
else:
print(request.POST)
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'lvjianzhao' and password == '123.com':
return HttpResponse('<h1>登录成功</h1>')
else:
return HttpResponse('<h1>登录失败</h1>')
3、login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>欢迎来到登录页面</h1>
<form action="/login/" method="post">
<label>
用户名:<input type="text" name="username">
</label>
<br/>
<label>
密 码:<input type="password" name="password">
</label>
<br/>
<input type="submit">
</form>
</body>
</html>
当我们访问项目进行登录是,登录成功后的页面如下:
可以看到,登录成功后的页面还是http://127.0.0.1:8000/login/
这个 url,也就是说,登录成功后,还是走的login视图函数返回的页面,那么在实际工作中,当登录成功后,就会跳转到项目首页或者某个页面,反正登录成功后的url肯定不是和登录使用的一个视图,也就是用到了重定向,待登录成功后,重定向到项目首页。
需要做的更改如下:
######### views.py文件
from django.shortcuts import render, HttpResponse, redirect
# 导入redirect模块
def login(request):
if request.method == 'GET':
return render(request, 'login.html')
else:
print(request.POST)
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'lvjianzhao' and password == '123.com':
return redirect('/home/') # 重定向到指定路径
else:
return HttpResponse('<h1>登录失败</h1>')
# 这里是首页的视图函数
def home(request):
return HttpResponse('<h1>欢迎访问此项目首页</h1>')
######### urls.py文件
from django.conf.urls import url
from apps import views
urlpatterns = [
url(r'^login/', views.login),
url(r'^home/', views.home), # 增加home的路由
]
再次访问项目并登录成功后,就可以重定向到正确的URL了,同时状态码也为“302”:
CBV和FBV
关于CBV和FBV相关概念,我都是看FBV和CBV这个视频来做的笔记,如果想更好的理解,建议直接看视频。
- FBV(function base views) 就是在视图里使用函数处理请求,如下:
# views.py文件内容
def login(request):
if request.method == 'GET':
return render(request, 'login.html')
else:
print(request.POST)
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'lvjianzhao' and password == '123.com':
return redirect('/home/')
else:
return HttpResponse('<h1>登录失败</h1>')
def home(request):
return HttpResponse('<h1>欢迎访问此项目首页</h1>')
- CBV(class base views) 就是在视图里使用类处理请求。
Python是一个面向对象的编程语言,如果只用函数来开发,有很多面向对象的优点就错失了(继承、封装、多态)。所以Django在后来加入了Class-Based-View。可以让我们用类写View。
这样做的优点主要下面两种:
- 提高了代码的复用性,可以使用面向对象的技术,比如Mixin(多继承)
- 可以用不同的函数针对不同的HTTP方法处理,而不是通过很多if判断,提高代码可读性
由于在之前的代码中,都是直接使用函数来处理请求的,所以这里就不说FBV了,一起来看下CBV是怎么写的。
下面是CBV的代码示例,但里面涉及到源码,只有真正理解源码,才会更熟练的去使用CBV,如果阅读源码能力较弱,可以看FBV和CBV这个视频,让人家带着去理解源码。
############## views.py文件内容如下:
from django.shortcuts import render, HttpResponse
# 导入View类
from django.views import View
# 需要继承 View类(这个类是django给提供的)
class MyView(View):
# 方法名必须使用get、post、put....等http支持的方法,具体缘由可以去看源码
# 对应的方法名,就是处理对应的get、post请求
def get(self, request):
return render(request, 'login.html')
def post(self, request):
return HttpResponse('<h1>登录成功</h1>')
############## urls.py文件内容如下:
from django.conf.urls import url
from apps import views
# 正则匹配后,写上自己的 views.类名.as_view() 为固定写法
urlpatterns = [
url(r'^login/', views.MyView.as_view()),
]
当我们理解CBV的源码后,会发现,CBV的关键是在dispatch这个调度方法上,那么我们就可以在dispatch这个方法上做些文章(在执行这个方法前,做一些事情),如下:
from django.shortcuts import render, HttpResponse
# 导入View类
from django.views import View
# 需要继承 View类(这个类是django给提供的)
class MyView(View):
# 如果需要在执行请求前后做一些事情,那么可以通过重写dispatch方法来实现
def dispatch(self, request, *args, **kwargs):
print('请求来了') # 调度之前做一些事情
ret = super().dispatch(request, *args, **kwargs) # 执行父类View中的dispatch方法,并用ret进行接收返回结果
print('请求处理完了') # 调度结束之后做的一些事情
# return 下父类中dispatch返回的结果
return ret
# 方法名必须使用get、post、put....等http支持的方法,具体缘由可以去看源码
# 对应的方法名,就是处理对应的get、post请求
def get(self, request):
print('处理请求中...')
return render(request, 'login.html')
def post(self, request):
print('处理请求中...')
return HttpResponse('<h1>登录成功</h1>')
给视图函数加装饰器
FBV加装饰器
# 示例1:
def n1(f):
def n2(*args,**kwargs):
print('请求之前')
ret = f(*args,**kwargs)
print('请求之后')
return ret
return n2
@n1
def home(request):
print('home!!!')
return render(request,'home.html')
# 示例2:
def is_login(f):
def inner(request, *args, **kwargs):
is_login = request.COOKIES.get('is_login')
print(is_login, type(is_login))
if is_login == 'True':
ret = f(request, *args, **kwargs)
return ret
else:
return redirect('login')
return inner
@is_login
def home(request):
return render(request, 'home.html')
CBV加装饰器
类中的方法与独立函数不完全相同,因此不能直接将函数装饰器应用于类中的方法 ,我们需要先将其转换为方法装饰器。
Django中提供了method_decorator装饰器用于将函数装饰器转换为方法装饰器。
from django.views import View
from django.utils.decorators import method_decorator # 导入method_decorator
from django.shortcuts import render, HttpResponse
# 装饰器
def n1(f):
def n2(*args, **kwargs):
print('请求之前')
ret = f(*args, **kwargs)
print('请求之后')
return ret
return n2
@method_decorator(n1, name='get') # 方式一
class LoginView(View):
# @method_decorator(n1) # 方式二
def dispatch(self, request, *args, **kwargs):
ret = super().dispatch(request, *args, **kwargs)
return ret
# @method_decorator(n1) # 方式三
def get(self, request):
print(f'{request.method}请求处理了')
return render(request, 'login.html')
def post(self, request):
username = request.POST.get('username')
password = request.POST.get('password')
print(username, password)
print(f'{request.method}请求处理了')
return HttpResponse('登录成功!')
关于上述三种给CBV添加装饰器的方式区别如下(方式一不推荐使用,方式二和方式三,可以根据需要去使用):
- 方式一:直接添加在类上,后面的name表示只给get添加装饰器。以这种方式如果想给多个方法加装饰器,需要写多层装饰器,因为name这个参数的值必须是个字符串,并且不能同时写两个方法,不推荐此种方式添加装饰器
- 方式二:直接添加在dispatch方法上,这样相当于每个方法都添加了装饰器(因为在处理请求前,都需要先执行dispatch方法);
- 方式三:添加在每一个函数中,哪个需要就给哪个添加。