Django视图类View源码分析
Django视图类View源码分析
一、视图函数
django中的视图函数,就是视图功能由函数实现。
响应:或渲染模板后返回HTML,或直接返回JSON数据。
参数:视图函数的第一个参数必须是request对象。
文本响应
from django.http import HttpRequest,HttpResponse,JsonResponse
def test_index(request:HttpRequest):
data = 'Test String'
return HttpResponse(data)
JSON响应
from django.http import HttpRequest, HttpResponse, JsonResponse
def test_index(request:HttpRequest):
data = [1, 2, 3]
return JsonResponse(data)
这里会抛异常,TypeError: In order to allow non-dict objects to be serialized set the safe
parameter to False.
。意思是,safe参数为False才可使用非字典数据,所以,除非有必要,否则还
是使用字典
from django.http import HttpRequest, HttpResponse, JsonResponse
def test_index(request:HttpRequest):
data = {'a':100, 'b':'abc'}
return JsonResponse(data)
请求方法限制装饰器
需求:如果需要对请求方法进行限制,例如:只允许GET方法请求,可以自己判断,也可以使用Django提供的装饰器函数。
自己判断:
from django.http import HttpResponse, HttpRequest, JsonResponse
def index(request:HttpRequest, *args, **kwargs):
print('~' * 30)
print(request, args, kwargs)
print(request.GET, request.method)
print('~' * 30)
if (request.method.lower() in ['get', 'post']):
return JsonResponse({'a':100, 'b':'abc'})
else:
return JsonResponse({}, status=405)
装饰器函数
from django.http import HttpRequest, HttpResponse, JsonResponse
from django.views.decorators.http import require_http_methods, require_GET,
require_POST, require_safe
# @require_http_methods(['GET', 'POST'])
# @require_GET
@require_POST
def test_index(request:HttpRequest):
data = {'a':100, 'b':'abc'}
return JsonResponse(data)
url.py
urlpatterns = [
path('',test_index), #路径映射,路径 为/emp
#as_view 当做视图函数,伪装成视图函数,封装成视图函数
# path('test/',TestView.as_view()),
path('dujie/',test_index)
]
测试过程中,当使用不被允许的方法请求时,会返回405状态码,表示Method Not Allowed
,如下图
装饰完后,test_index 就是新的视图函数,装饰器内部的inner函数。
二、视图类
视图类就是视图功能由一个类和其方法实现
参考:https://docs.djangoproject.com/en/3.2/topics/class-based-views/
View类实现原理分析
可以看到官方文档上实现视图类,在urlpatterns定义中,路径映射的应该是函数,而AboutView是一个类并不是函数,所以他使用的是AboutView.as_view() ,下面来分析下,
一级路由配置如下:
# 主路由,一级路由,根路由
urlpatterns = [
path('emp/', include('employee.urls')) # /emp/
]
employee/urls.py如下
# 二级路由,应用路由
from django.urls import path
from .views import TestView # 视图类
urlpatterns = [
path('test/', TestView.as_view()), # /emp/test/
]
django.views.View类,定义了http的方法的小写名称列表,这些小写名称其实就是处理请求方法名的小写。
View类的类方法as_view()方法调用后返回一个内建的view(request,args,*kwargs)新函数,本质上,其实url还是映射到这个函数上。
请求request到来后,直接发给TestView.as_view() 函数,TestView.as_view()函数内部
- 构建TestView实例self。下面可以看源码,每一个请求都会创建一个实例
- dispath派发请求,self.dispath(request,args,*kwargs)
dispath方法内部对比请求方法method,如果存在请求的get、post等方法,则调用,否则返回405,
本质上,as_view()方法还是把一个类封装成一个视图函数。
这个视图函数,内部使用了一个分发函数,使用请求方法名称把请求分发给存在的同名函数处理
as_view源码分析
##请求方法列表
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
## classonlymethod 规定了as_view只能由class调用,所以只能是TestView.as_view()这么调用
@classonlymethod
## 这里的第一个参数cls就是TestView
def as_view(cls, **initkwargs):
## 这个函数其实就是url真正映射的视图函数,第一个参数必须是request
def view(request, *args, **kwargs):
## 这里就是将传入到class类实例化,每一个请求都会实例化一次,互补干扰
self = cls(**initkwargs)
self.setup(request, *args, **kwargs)
if not hasattr(self, 'request'):
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
##dispath 派发请求
return self.dispatch(request, *args, **kwargs)
## 将视图函数返回
return view
def dispatch(self, request, *args, **kwargs):
# Try to dispatch to the right method; if a method doesn't exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn't on the approved list.
## 这里判断请求的类型是否在定义好的method_names列表中
if request.method.lower() in self.http_method_names:
#如果存在则调用反射函数,判断是否有相关get、post等方法,如果没有则调用默认值函数self.http_method_not_allowed返回405
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
#如果请求方法不存在定义好的列表中,则直接调用notallow函数返回405
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
def http_method_not_allowed(self, request, *args, **kwargs):
logger.warning(
'Method Not Allowed (%s): %s', request.method, request.path,
extra={'status_code': 405, 'request': request}
)
return HttpResponseNotAllowed(self._allowed_methods())
视图类实现
from django.http import HttpRequest, HttpResponse, JsonResponse
from django.views import View
class TestIndex(View):
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
def get(self, request): # 支持GET
data = {'a':100, 'b':'abc'}
return JsonResponse(data)
def post(self, request): # 支持POST
data = {'a':200, 'b':'xyz'}
return JsonResponse(data)