Django——CBV源码解析

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方法,没有就走它默认的
相关推荐
nbsaas-boot31 分钟前
Java 正则表达式白皮书:语法详解、工程实践与常用表达式库
开发语言·python·mysql
仗剑_走天涯33 分钟前
基于pytorch.nn模块实现线性模型
人工智能·pytorch·python·深度学习
chao_78935 分钟前
二分查找篇——搜索旋转排序数组【LeetCode】两次二分查找
开发语言·数据结构·python·算法·leetcode
chao_7895 小时前
二分查找篇——搜索旋转排序数组【LeetCode】一次二分查找
数据结构·python·算法·leetcode·二分查找
烛阴5 小时前
Python装饰器解除:如何让被装饰的函数重获自由?
前端·python
noravinsc6 小时前
django 一个表中包括id和parentid,如何通过parentid找到全部父爷id
python·django·sqlite
ajassi20006 小时前
开源 python 应用 开发(三)python语法介绍
linux·python·开源·自动化
沉默媛6 小时前
如何安装python以及jupyter notebook
开发语言·python·jupyter
Deng9452013148 小时前
基于Python的旅游数据可视化应用
python·numpy·pandas·旅游·数据可视化技术
2401_878624798 小时前
pytorch 自动微分
人工智能·pytorch·python·机器学习