Django——CBV源码解析
以下是views模块调用as_view()方法的代码示例
# urls.py
from django.contrib import admin
from django.urls import path
import app.viewsurlpatterns = [path('admin/', admin.site.urls),path('app/', app.views.task.as_view()),
]
# views.py
class task(View):# 根据id获取def get(self, request, u_id):response = {'code': '200', 'msg': "查询成功"}return JsonResponse(json.dumps(response), safe=False)
该功能实现的是当有get请求发送到app这个接口时会返回一个response
字典
首先引入问题:为什么浏览器向后端发送get请求时会被该get方法精准接受?
其实是因为在注册url时app.views调用的as_view()方法帮我们做好了大部分规划
Ctrl+左键进入as_view()源码
class View:http_method_names = ["get","post","put","patch","delete","head","options","trace",]def __init__(self, **kwargs):for key, value in kwargs.items():setattr(self, key, value)@classonlymethoddef as_view(cls, **initkwargs):"""Main entry point for a request-response process."""for key in initkwargs:if key in cls.http_method_names:raise TypeError("The method name %s is not accepted as a keyword argument ""to %s()." % (key, cls.__name__))if not hasattr(cls, key):raise TypeError("%s() received an invalid keyword %r. as_view ""only accepts arguments that are already ""attributes of the class." % (cls.__name__, key))def view(request, *args, **kwargs):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__)return self.dispatch(request, *args, **kwargs)view.view_class = clsview.view_initkwargs = initkwargs# __name__ and __qualname__ are intentionally left unchanged as# view_class should be used to robustly determine the name of the view# instead.view.__doc__ = cls.__doc__view.__module__ = cls.__module__view.__annotations__ = cls.dispatch.__annotations__# Copy possible attributes set by decorators, e.g. @csrf_exempt, from# the dispatch method.view.__dict__.update(cls.dispatch.__dict__)# Mark the callback if the view class is async.if cls.view_is_async:markcoroutinefunction(view)return viewdef 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_allowedreturn 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},)response = HttpResponseNotAllowed(self._allowed_methods())if self.view_is_async:async def func():return responsereturn func()else:return response
@classonlymethod
表示只能用类调用此方法,这也是为什么我们只能用as_views()而不是as_views- 这个时候我们来到了task(View)继承的View类下的as_view()方法
- 中间的步骤先不管 直接看
return view
def view(request, *args, **kwargs):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__)return self.dispatch(request, *args, **kwargs)
- 这个时候可以看出其实我们就是在调用父类的view方法
- 这里的request参数就是我们的浏览器接受的request请求,如果没填request则会弹出一个error
- 重点是最后调用了实例中的dispatch方法
- 既然我们的task类调用了dispatch方法那么就应该在task类下搜寻这个方法,但是很明显我们没有写过这方法,因此又回到父类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_allowedreturn handler(request, *args, **kwargs)
if request.method.lower() in self.http_method_names:
当我们的request请求类型存在于http_method_names
时- 那么先看看这个
http_method_names
是什么东西
http_method_names = ["get","post","put","patch","delete","head","options","trace",
]
- 其实就是个定义好的字符串列表
- 再接着看
dispatch
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
- 其实就是从我们task实例中获取相应的HTTP请求方法,如果不存在就用它默认的
- 最后返回handler,再解释一下gatter的用法
class Test(object):x = 1a = Test()
print(getattr(a, 'x')) # 获取属性 x 值
# 结果:1
print(getattr(a, 'y', 'None')) # 获取属性 y 值不存在,但设置了默认值
# 结果:None
print(a.x) # 效果等同于上面
# 结果:1
- 回到我们最初的问题
为什么浏览器向后端发送get请求时会被该get方法精准接受?
- 走到这里基本可以得出结论了,说白了就是如果我有get就走我类下的get方法,没有就走它默认的