Django 类视图(CBV)源码级解析 ——从浏览器敲下回车到真正执行 `get()` / `post()` 的全过程

举报
周杰伦本人 发表于 2025/08/31 21:09:19 2025/08/31
【摘要】 Django 类视图(CBV)源码级解析——从浏览器敲下回车到真正执行 get() / post() 的全过程 一、as_view():类方法返回真正的「视图函数」django/views/generic/base.py View 类# django/views/generic/base.py View 类@classonlymethoddef as_view(cls, **initk...

Django 类视图(CBV)源码级解析

——从浏览器敲下回车到真正执行 get() / post() 的全过程


一、as_view():类方法返回真正的「视图函数」

django/views/generic/base.py View 类

# django/views/generic/base.py  View 类
@classonlymethod
def as_view(cls, **initkwargs):
    """
    入口:urls.py 里写的 LoginView.as_view()
    返回值是一个闭包 view——也就是 Django 最终调用的「视图函数」。
    """
    # 省略若干参数检查 …
    def view(request, *args, **kwargs):
        # 1. 实例化当前类 cls(**initkwargs)  =>  LoginView()
        self = cls(**initkwargs)
        self.setup(request, *args, **kwargs)
        # 2. 把 request、*args、**kwargs 挂到实例上
        if not hasattr(self, 'request'):
            raise AttributeError("…")
        # 3. 进入「分发」逻辑
        return self.dispatch(request, *args, **kwargs)

    # 4. 给闭包加一些元数据,方便调试
    view.view_class = cls
    view.view_initkwargs = initkwargs
    return view          # ← urls.py 真正拿到的就是这个 view
  1. 实例化当前类 cls(**initkwargs) => LoginView()

  2. 把 request、*args、**kwargs 挂到实例上

  3. 进入「分发」逻辑

  4. 给闭包加一些元数据,方便调试

要点:

  • as_view() 带括号是因为要 立即执行 并返回闭包 view

  • 闭包内部把 cls(例如 LoginView)实例化成 self,后面所有逻辑都围绕这个实例展开。

那我们再看看dispatch()方法做了什么


二、dispatch():HTTP 方法的分发器

# 仍在 View 类中
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

def dispatch(self, request, *args, **kwargs):
    # 1. 取小写方法名
    method = request.method.lower()

    # 2. 判断是否在白名单
    if method in self.http_method_names:
        # 3. 反射:去实例上找同名方法,找不到就兜底 http_method_not_allowed
        handler = getattr(self, method, self.http_method_not_allowed)
    else:
        handler = self.http_method_not_allowed

    # 4. 真正执行 get() / post() / put()return handler(request, *args, **kwargs)
  1. 取小写方法名

  2. 判断是否在白名单

  3. 如果在反射:去实例上找同名方法,如果不在找不到就兜底 http_method_not_allowed

  4. 真正执行 get() / post() / put() …

要点:

  • 405 的由来:当 method 不在 http_method_names 子类没有实现同名方法时,handler 指向 http_method_not_allowed,返回 HttpResponseNotAllowed(status=405)

  • 反射语句 getattr(self, method, ...) 保证了 LoginView.get / LoginView.post 会被精准调用。


三、整体时序图(浏览器 → Django → 你的代码)

  1. 浏览器 GET /login/

  2. Django 路由匹配 → 执行 LoginView.as_view() 返回的闭包 view(request)

  3. view 内部:
    self = LoginView() —— 实例化
    self.dispatch(request) —— 方法分发
    dispatch 发现 request.method == 'GET' ⇒ 找到并执行 self.get(request)

  4. get() 返回 HttpResponse,浏览器收到 200 OK


四、常见面试追问 & 一句话答案

  • 为什么必须写 .as_view() 且要带括号?
    → 它是类方法,返回真正的视图闭包;带括号才能立即执行并拿到闭包。

  • 405 是哪里抛的?
    dispatch 在用 getattr 查找方法时找不到,就 fallback 到 http_method_not_allowed

  • clsself 各代表谁?
    cls 是类本身(LoginView),self 是本次请求实例化的 LoginView 对象。

背住以上三句话,再读 CBV 源码即可如履平地。源码阅读能让我们刨根问题,学习作者的思想,对面试也有帮助。。还有就是看源码一定不要陷进去。。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。