Django中的视图
Django之视图
一个视图函数(类),简称视图,是一个简单的python函数(类),它接收web请求并返回web响应。响应可以是一个网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片。
无论视图本身包含什么逻辑,都要返回响应。代码写在哪里无所谓,只要它在你当前项目目录下面,除此之外,没有更多的要求了。
为了将代码放在某处,大家约定俗成将视图放置在项目(project)或应用程序(app)目录中的名为views.py的文件当中。
1. 一个简单的视图
以下是一个以HTML文档的形式返回当前日期和时间的视图:
from django.http import HttpResponse
import datetimedef current_datetime(request):now = datetime.datetime.now()html = "<html><body>It is now %s.</body></html>" % nowreturn HttpResponse(html)
让我们来逐行解释下上面的代码:
-
首先,我们从
django.http
模块导入了HttpResponse
类,以及Python的datetime
库。 -
接着,我们定义了
current_datetime
函数。它就是视图函数。每个视图函数都使用HttpRequest
对象作为第一个参数,并且通常称之为request
。注意,视图函数的名称并不重要;不需要用一个统一的命名方式来命名,以便让Django识别它。我们将其命名为
current_datetime
,是因为这个名称能够比较准确地反映出它实现的功能。 -
这个视图会返回一个
HttpResponse
对象,其中包含生成的响应。每个视图函数都负责返回一个HttpResponse
对象。
Django使用请求和响应对象来通过系统传递状态。
当浏览器向服务端请求一个页面时,Django创建一个HttpRequest对象,该对象包含关于请求的元数据。然后,Django加载相应的视图,将这个HttpRequest对象作为第一个参数传递给视图函数。
每个视图负责返回一个HttpResponse对象。
2. FBV和CBV
定义
FBV(function based view):基于函数的视图
CBV(class based view):基于类的视图
区别
FBV:把所有的处理逻辑都写在函数中,在函数中判断前端发来的请求方式时POST还是GET,然后执行对应的逻辑
CBV:在一个CBV的视图中,将不同请求的方法分别定义成类下面的不同函数;这样前端发来的请求执行类中对应的函数即可
2.1 FBV
FBV视图
from django.shortcuts import render,redirect,reverse
from app01 import models# 增加出版社
def publisher_add(request):# POST请求方式的处理逻辑if request.method == 'POST':pub_name = request.POST.get('pub_name') # key名对应input标签中name字段的值if not pub_name:return render(request,'publisher_add.html',{'error':'输入不能为空'})ret = models.Publisher.objects.create(name=pub_name) # 返回的是新插入的对象# 跳转至展示页面return redirect(reverse('app01:pub'))# GET请求处理逻辑return render(request,'publisher_add.html') # get请求的处理逻辑
URLconf
url(r'^/publisher/add/$',views.publisher_add,name="pub_add"),
2.2 CBV
CBV视图
from django.shortcuts import render,redirect,reverse
from app01 import models
from django.views import View # 需要导入View基类,进行继承 class PublisherAdd(View):# 处理GET请求的逻辑def get(self,request,*args,**kwargs):return render(request, 'publisher_add.html') # get请求的处理逻辑# 处理POST请求的逻辑def post(self,request,*args,**kwargs):pub_name = request.POST.get('pub_name') # key名对应input标签中name字段的值if not pub_name:return render(request,'publisher_add.html',{'error':'输入不能为空'})ret = models.Publisher.objects.create(name=pub_name) # 返回的是新插入的对象# 跳转至展示页面return redirect(reverse('app01:pub'))
URLconf
使用CBV时,URLconf的写法也要有所变化
url(r'^/publisher/add/$',views.PublisherAdd.as_view(),name="pub_add"),
3. 给视图加装饰器
3.1 使用装饰器装饰FBV
如给publisher_list
视图函数加一个计算函数执行所用时间的装饰器
from app01 import models
from django.views import View
import time
from functools import wrapsdef timmer(func):@wraps(func)def inner(*args,**kwargs):start = time.time()ret = func(*args,**kwargs)print('{}函数执行的时间是:{}'.format(func.__name__,time.time()-start))return retreturn inner@timmer
def publisher_list(request):# 查询所有的出版社对象all_publishers = models.Publisher.objects.all()# 将结果返回给页面return render(request,'publisher_list.html',{'all_publishers':all_publishers})
访问publisher_list.html页面之后,触发publisher_list
函数,后台打印内容如下
publisher_list函数执行的时间是:0.015687942504882812
3.2 使用装饰器装饰CBV
类中的方法与独立函数不完全相同,因此不能直接将函数装饰器应用与类中的方法,我们需要先将其转化为方法装饰器
Django中提供了method_decorator
装饰器用于将函数装饰器转换为方法装饰器:通过from django.utils.decorators import method_decorator
推荐使用
CBV添加装饰器的方法有三种,如下(另,仍引用上述的timmer
装饰器)
1)加在方法上
from django.shortcuts import render,redirect,reverse
from app01 import models
from django.views import View
import time
from functools import wraps
from django.utils.decorators import method_decoratordef timmer(func):@wraps(func)def inner(*args,**kwargs):start = time.time()ret = func(*args,**kwargs)print('{}函数执行的时间是:{}'.format(func.__name__,time.time()-start))return retreturn innerclass PublisherAdd(View):# 处理GET请求的逻辑@method_decorator(timmer)def get(self,request,*args,**kwargs):return render(request, 'publisher_add.html') # get请求的处理逻辑# 处理POST请求的逻辑@method_decorator(timmer)def post(self,request,*args,**kwargs):# 获取提交的数据pub_name = request.POST.get('pub_name') # key名对应input标签中name字段的值# 校验提交数据是否为空if not pub_name:return render(request,'publisher_add.html',{'error':'输入不能为空'})# 插入数据库print(pub_name,type(pub_name)) # 太空出版社 <class 'str'>ret = models.Publisher.objects.create(name=pub_name) # 返回的是新插入的对象print(ret,type(ret)) # Publisher object <class 'app01.models.Publisher'># 跳转至展示页面return redirect(reverse('app01:pub'))
触发PublisherAdd
逻辑中的GET与POST请求,后台打印结果如下:
bound_func函数执行的时间是:0.0005960464477539062
[11/Feb/2020 15:43:42] "GET /app01/publisher/add/ HTTP/1.1" 200 338
[11/Feb/2020 15:43:47] "POST /app01/publisher/add/ HTTP/1.1" 200 356
bound_func函数执行的时间是:0.0006589889526367188
2)加在dispath方法上
-
自己在类中写dispath方法,继承父类等dispath
class PublisherAdd(View):# 处理GET请求的逻辑@method_decorator(timmer)def dispatch(self, request, *args, **kwargs):ret = super().dispatch(request,*args,**kwargs)return retdef get(self,request,*args,**kwargs):return render(request, 'publisher_add.html') # get请求的处理逻辑# 处理POST请求的逻辑def post(self,request,*args,**kwargs):# 获取提交的数据pub_name = request.POST.get('pub_name') # key名对应input标签中name字段的值# 校验提交数据是否为空if not pub_name:return render(request,'publisher_add.html',{'error':'输入不能为空'})# 插入数据库print(pub_name,type(pub_name)) # 太空出版社 <class 'str'>ret = models.Publisher.objects.create(name=pub_name) # 返回的是新插入的对象print(ret,type(ret)) # Publisher object <class 'app01.models.Publisher'># 跳转至展示页面return redirect(reverse('app01:pub'))
触发
PublisherAdd
逻辑中的GET与POST请求,后台打印结果如下:bound_func函数执行的时间是:0.0013780593872070312 [11/Feb/2020 15:48:47] "GET /app01/publisher/add/ HTTP/1.1" 200 338 [11/Feb/2020 15:48:51] "POST /app01/publisher/add/ HTTP/1.1" 200 356 bound_func函数执行的时间是:0.0006721019744873047
-
直接通指定加在dispath方法上
@method_decorator(timmer,name='dispatch') class PublisherAdd(View):# 处理GET请求的逻辑def get(self,request,*args,**kwargs):return render(request, 'publisher_add.html') # get请求的处理逻辑# 处理POST请求的逻辑def post(self,request,*args,**kwargs):# 获取提交的数据pub_name = request.POST.get('pub_name') # key名对应input标签中name字段的值# 校验提交数据是否为空if not pub_name:return render(request,'publisher_add.html',{'error':'输入不能为空'})# 插入数据库print(pub_name,type(pub_name)) # 太空出版社 <class 'str'>ret = models.Publisher.objects.create(name=pub_name) # 返回的是新插入的对象print(ret,type(ret)) # Publisher object <class 'app01.models.Publisher'># 跳转至展示页面return redirect(reverse('app01:pub'))
触发
PublisherAdd
逻辑中的GET与POST请求,后台打印结果如下:bound_func函数执行的时间是:0.0013499259948730469 [11/Feb/2020 15:52:11] "GET /app01/publisher/add/ HTTP/1.1" 200 338 bound_func函数执行的时间是:0.0006899833679199219 [11/Feb/2020 15:52:13] "POST /app01/publisher/add/ HTTP/1.1" 200 356
3)加在类上
@method_decorator(timmer,name='get')
@method_decorator(timmer,name='post')
class PublisherAdd(View):# 处理GET请求的逻辑def get(self,request,*args,**kwargs):return render(request, 'publisher_add.html') # get请求的处理逻辑# 处理POST请求的逻辑def post(self,request,*args,**kwargs):# 获取提交的数据pub_name = request.POST.get('pub_name') # key名对应input标签中name字段的值# 校验提交数据是否为空if not pub_name:return render(request,'publisher_add.html',{'error':'输入不能为空'})# 插入数据库print(pub_name,type(pub_name)) # 太空出版社 <class 'str'>ret = models.Publisher.objects.create(name=pub_name) # 返回的是新插入的对象print(ret,type(ret)) # Publisher object <class 'app01.models.Publisher'># 跳转至展示页面return redirect(reverse('app01:pub'))
触发PublisherAdd
逻辑中的GET与POST请求,后台打印结果如下:
[11/Feb/2020 15:54:20] "GET /app01/publisher/add/ HTTP/1.1" 200 338
bound_func函数执行的时间是:0.0005879402160644531
[11/Feb/2020 15:54:23] "POST /app01/publisher/add/ HTTP/1.1" 200 356
bound_func函数执行的时间是:0.0006330013275146484
注意:推荐使用method_decorator
4. request对象
当一个页面被请求时,Django就会创建一个包含本次请求元信息的HttpRequest对象。
Django会将这个对象自动传递给响应的视图函数,一般视图函数约定俗成地使用request参数承接这个对象。
《官方文档连接》
也就是说,一次请求的所有信息都被封装在了HttpRequest
对象中,而在Django中通过request
参数接收此对象,想要获取什么信息,直接从这个参数中获取即可。
4.1 request属性
常用的属性:
request.method # 获取请求方法 GET POST
request.GET # 获取url上的查询参数 /dd/?id=1&name=tom 注:与请求方式无关
request.POST # 获取post请求方法提交的数据 {k1:v1,k2:v2} 前端常用form表单提交,urlencode
request.path_info # 返回当前路径信息,不包含IP、端口和参数 /app01/publisher/add/
request.body # 获取请求提byte类型 b'pub_name=China%E5%87%BA%E7%89%88%E7%A4%BE'
request.COOKIES # 获取cookie的数据
request.session # 获取session数据
request.FILES # 获取上传的文件
request.META # 获取头信息
如上,即request的常用属性,当然除此之外还有其他属性,此处不一一列举,有需要可参考官方文档即可:.11/ref/request-response/
此处需要注意的是:
1. Django将请求报文中的请求行、头部信息、内容主体封装成HttpRequest
类中的属性
2. 所有属性应该被认为是只读的,除非另有说明
文件上传示例
HttpRequest.Files一个类似于字典的对象,包含所有的上传文件信息。FILES 中的每个键为<input type="file" name="" /> 中的name,值则为对应的数据。注意,FILES 只有在请求的方法为POST 且提交的<form> 带有enctype="multipart/form-data" 的情况下才会
包含数据。否则,FILES 将为一个空的类似于字典的对象。
def upload(request):"""保存上传文件前,数据需要存放在某个位置。默认当上传文件小于2.5M时,django会将上传文件的全部内容读进内存。从内存读取一次,写磁盘一次。但当上传文件很大时,django会把上传文件写到临时文件中,然后存放到系统临时文件夹中。:param request: :return: """if request.method == "POST":# 从请求的FILES中获取上传文件的文件名,file为页面上type=files类型input的name属性值filename = request.FILES["file"].name# 在项目目录下新建一个文件with open(filename, "wb") as f:# 从上传的文件对象中一点一点读for chunk in request.FILES["file"].chunks():# 写入本地文件f.write(chunk)return HttpResponse("上传OK")
4.2 request方法
-
HttpRequest.get_host()
获取主机的IP与端口信息,例如 127.0.0.1:8000
-
HTTPRequest.get_full_path()
获取路径信息,与path_info属性对比,ge t_full_path方法包含请求参数
-
HttpRequest.is_secure()
判断请求是否安全,如果请求是安全的,则返回True;即请求是通过HTTPS发起的
-
HttpRequest.is_ajax()
判断请求是否是ajax请求,如果是则返回True,否则返回False
注意:
以上仅常用方法介绍,其他方法学习可参考官网
5. response对象
与由Django自动创建的 HttpRequest对象相比,HttpResponse对象是我们的职责范围了。我们写的每个视图都需要实例化,填充和返回一个HttpResponse。
5.1 使用方法
传递字符串
from django.http import HttpResponse
response = HttpResponse("this is a test page")
response = HttpResponse("Text only, please.", content_type="text/plain")
设置或删除响应头信息
response = HttpResponse()
response['Content-Type'] = 'text/html; charset=UTF-8'
del response['Content-Type']
5.2 response的属性
HttpResponse.content # 响应内容HttpResponse.charset # 响应内容的编码HttpResponse.status_code # 响应的状态吗
6. JsonResponse对象
JsonReponse是HttpResponse的子类,专门用来生成JSON编码的响应,相当于将json转换和响应的动作的封装
例如,前后端分离项目,前后端仅靠json格式数据进行交互
from django.http.response import JsonResponsedef json_data(request):msg = {'name':'tom','age':18}return JsonResponse(msg) # Content-Type: application/json
默认只能传递字典类型,如果要传递非字典类型(如列表)需要设置一下safe关键字参数
response = JsonResponse([1, 2, 3], safe=False)
7. Django shortcut functions
《官方文档连接》
from django.shortcuts import render,redirect,reverse
7.1 render
1)render函数定义
def render(request, template_name, context=None, content_type=None, status=None, using=None):"""Returns a HttpResponse whose content is filled with the result of callingdjango.template.loader.render_to_string() with the passed arguments."""content = loader.render_to_string(template_name, context, request, using=using)return HttpResponse(content, content_type, status)
可知:render方法,结合一个给定的模版和一个给定的上下文字典,并返回一个渲染后的HttpResponse对象
2)参数介绍
- request:用于生成响应的请求对象
- template_name:要使用的模版文件的完整名称,可选的参数
- context:添加到模版上下文的一个字典。默认是一个空字典。如果字典中的某个值是可调用的,视图将在渲染模版之前调用它
- content_type:生成的文档要使用的MIME类型。默认为DEFAULT_CONTENT_TYPE设置的值。默认为"text/html"
- status:响应的状态码。默认为200
- useing:用于家在模版的模版引擎的名称
3)使用示例
from django.shortcuts import renderdef my_view(request):# 视图的代码写在这里return render(request, 'myapp/index.html', {'foo': 'bar'})
上述代码等价于:
from django.http import HttpResponse
from django.template import loaderdef my_view(request):# 视图代码写在这里t = loader.get_template('myapp/index.html')c = {'foo': 'bar'}return HttpResponse(t.render(c, request))
7.2 redirect
redirect重定向本质:就是响应头 Location:地址
1)redirect函数定义
def redirect(to, *args, **kwargs):"""Returns an HttpResponseRedirect to the appropriate URL for the argumentspassed.The arguments could be:* A model: the model's `get_absolute_url()` function will be called.* A view name, possibly with arguments: `urls.reverse()` will be usedto reverse-resolve the name.* A URL, which will be used as-is for the redirect location.By default issues a temporary redirect; pass permanent=True to issue apermanent redirect"""if kwargs.pop('permanent', False):redirect_class = HttpResponsePermanentRedirectelse:redirect_class = HttpResponseRedirectreturn redirect_class(resolve_url(to, *args, **kwargs))
2)参数介绍
其中redirect函数的参数可以为如下:
- 一个模型:将调用模型的get_absolute_url()函数
- 一个视图,可以带有参数:将使用urlresolvers.reverse来反向解析名称
- 一个绝对的或相对的URL,将原封不动的作为重定向的位置
默认返回一个临时的重定向;传递Permanent=True
可以返回一个永久的重定向
3)使用示例
有多种方式来使用redirect函数
传递一个具体的ORM对象
from django.shortcuts import redirectdef my_view(request):...object = MyModel.objects.get(...)return redirect(object)
传递一个视图的名称
def my_view(request):...return redirect('some-view-name', foo='bar')
传递要重定向到的一个具体的网址
def my_view(request):...return redirect('/some/url/')
传递要重定向到的一个完整的网址
def my_view(request):...return redirect('/')
默认情况下,redirect返回一个临时重定向,以上所有的形式都接收一个permanent参数;如果设置为True,将返回一个永久的重定向
def my_view(request):...object = MyModel.objects.get(...)return redirect(object, permanent=True)
扩展:
临时重定向(响应状态吗:302)和永久重定向(响应状态吗:301)对普通用户来说是没什么区别的,它主要是面向的是搜索引擎的机器人。
A页面临时重定向到B页面,那么搜索引擎收录的就是A页面;
A页面永久重定向到B页面,那么搜索引擎收录的就是B页面。
发布评论