django中的View源码解析
FBV: path('admin/', admin.site.urls),
CBV: path('books/', views.BookView.as_view()),
在路由层使用CBV时需要执行 CBV.as_view()方法,使该位置的实参变为一个FBV(函数地址)
as_view方法的返回值是view
view是一个函数,即CBV的views.BookView.as_view()的结果就是views.BookView.view
在收到请求,路由匹配成功之后会触发view函数的运行
view函数 执行了self.dispatch(request, *args, **kwargs)并返回该函数的返回值
class View:
def as_view(cls, **initkwargs):
def view(request, *args, **kwargs):
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
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__
)
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
# take name and docstring from class
update_wrapper(view, cls, updated=())
# and possible attributes set by decorators
# like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=())
return view
进入dispatch方法查看源码
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.
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
dispatch中对请求方式是否在http_method_names列表中进行了判断
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
如果请求方式在这个列表中则进行反射,从我们自定义的CBV中取出与请求方式同名的函数执行,
如果CBV中没该名字的函数或者请求方式不在该列表中,就会报错,拒绝访问
drf中APIView源码解析
FBV: path('admin/', admin.site.urls),
CBV: path('books/', views.BookAPIView.as_view()),
在路由层使用CBV时需要执行 CBV.as_view()方法,使该位置的实参变为一个FBV(函数地址)
看源码可知执行该方法得到的返回值为csrf_exempt(view)
装饰器的语法糖的原理就是view=csrf_exempt(view),
所以返回csrf_exempt(view)也可以看作是 为view添加了一个csrf_exempt装饰器
`
APIView继承了django中的View类
class APIView(View):
@classmethod
def as_view(cls, **initkwargs):
view = super().as_view(**initkwargs)
view.cls = cls
view.initkwargs = initkwargs
return csrf_exempt(view)
在执行APIView的as_view方法时调用了父类的as_view方法,也就是View的as_view方法
class View:
@classonlymethod
def as_view(cls, **initkwargs):
def view(request, *args, **kwargs):
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
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__
)
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
# take name and docstring from class
update_wrapper(view, cls, updated=())
# and possible attributes set by decorators
# like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=())
return view
View的as_view方法执行并返回了self.dispatch(request, *args, **kwargs)
注意:这里的self指代的是自己写的CBV的对象
所以self.dispatch(request, *args, **kwargs)触发的不是View中的dispatch方法
按照名称空间的属性查找可以知道,它是先从自己写的CBV的对象中查看是否有dispatch方法
如果没有则执行父类,也就是APIView中的dispatch方法
class APIView(View):
def dispatch(self, request, *args, **kwargs):
"""
`.dispatch()` is pretty much the same as Django's regular dispatch,
but with extra hooks for startup, finalize, and exception handling.
"""
self.args = args
self.kwargs = kwargs
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
self.initial(request, *args, **kwargs)
# Get the appropriate handler method
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
response = handler(request, *args, **kwargs)
except Exception as exc:
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
APIView中的dispatch方法基本的功能和View中的dispatch方法一致,但是添加了一些别的功能:
-
request的封装 request = self.initialize_request(request, *args, **kwargs) self.request = request
对request进行了封装:将原生django中的request传入initialize_request方法,通过实例化drf定义的Request类,产生了一个新的request对象,进行替换,在原本的request对象的功能基础上,额外添加了一些功能,例如request.data属性
-
异常处理 except Exception as exc: response = self.handle_exception(exc)
对异常进行捕获,使我们可以重写handle_exception方法,返回json格式的数据
-
response的封装 self.response = self.finalize_response(request, response, *args, **kwargs) return self.response
对response对象进行封装,添加了根据请求来源返回不同结果的功能