Django------CBV源码解析
以下是views模块调用as_view()方法的代码示例
python
# urls.py
from django.contrib import admin
from django.urls import path
import app.views
urlpatterns = [
path('admin/', admin.site.urls),
path('app/', app.views.task.as_view()),
]
python
# 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()源码
python
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)
@classonlymethod
def 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 = cls
view.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 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.
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)
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 response
return func()
else:
return response
@classonlymethod
表示只能用类调用此方法,这也是为什么我们只能用as_views()而不是as_views- 这个时候我们来到了task(View)继承的View类下的as_view()方法
- 中间的步骤先不管 直接看
return view
python
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方法(这俩方法挨得很近,往下翻翻就找到了)
python
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)
if request.method.lower() in self.http_method_names:
当我们的request请求类型存在于http_method_names
时- 那么先看看这个
http_method_names
是什么东西
python
http_method_names = [
"get",
"post",
"put",
"patch",
"delete",
"head",
"options",
"trace",
]
- 其实就是个定义好的字符串列表
- 再接着看
dispatch
python
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
- 其实就是从我们task实例中获取相应的HTTP请求方法,如果不存在就用它默认的
- 最后返回handler,再解释一下gatter的用法
python
class Test(object):
x = 1
a = Test()
print(getattr(a, 'x')) # 获取属性 x 值
# 结果:1
print(getattr(a, 'y', 'None')) # 获取属性 y 值不存在,但设置了默认值
# 结果:None
print(a.x) # 效果等同于上面
# 结果:1
- 回到我们最初的问题
为什么浏览器向后端发送get请求时会被该get方法精准接受?
- 走到这里基本可以得出结论了,说白了就是如果我有get就走我类下的get方法,没有就走它默认的