游乐游手机版
首页/编程语言/文章详情

Django CBV源码解读:请求如何定位get方法

时间:2026-06-27 06:41
DjangoCBV中,`as_view()`将类转换为函数供路由调用,每次请求实例化新对象。`dispatch()`通过`getattr`根据请求方法名动态调用对应方法,未定义则返回405。`APIView`在此基础上包装请求对象,自动执行认证、权限、限流和异常处理。

Django CBV 源码解析:一个 HTTP 请求如何精准定位到你的 get() 方法


刚从前端转到 Django 开发的同学,初看这种写法时往往会感到困惑:

Django CBV 源码解读:一个请求是怎么找到你的 get() 方法的

 复制代码class TestView(APIView):
    def get(self, request):
        return Response({"message": "hello"})

URL 配置中是这样注册的:

 复制代码from django.urls import path
from . import viewsurlpatterns = [
    path("test/", views.TestView.as_view()),
]

疑问随之而来:

  • as_view() 究竟做了什么?为什么不能直接传入 TestView 类?
  • HTTP 请求进入后,框架是如何自动匹配到 get() 方法的?
  • 如果我在类中定义了 post(),POST 请求又是如何准确调用它的?

带着这些疑问深入阅读源码后,一切豁然开朗。


FBV 与 CBV 的对比

先了解背景知识。Django 编写视图有两种主流方式:

FBV(函数视图)

 复制代码def test_view(request):
    if request.method == 'GET':
        return JsonResponse({"message": "hello"})
    if request.method == 'POST':
        name = request.POST.get('name')
        return JsonResponse({"message": f"hello {name}"})

CBV(类视图)

 复制代码class TestView(APIView):
    def get(self, request):
        return Response({"message": "hello"})    def post(self, request):
        name = request.data.get('name')
        return Response({"message": f"hello {name}"})

CBV 的优势十分显著:

  • 告别大量 if request.method == 条件判断,每个 HTTP 方法对应独立函数,职责划分明确
  • 支持继承与复用,APIView 内置了认证、权限、解析器等通用逻辑
  • 代码架构更加符合面向对象设计原则

第一个核心问题:as_view() 的作用是什么

URL 注册时使用的是:

 复制代码path('test/', TestView.as_view()),

而不是:

 复制代码path('test/', TestView),

原因何在?

Django 的 URL 路由系统期望得到一个可调用函数,用于处理请求。但 TestView 是一个,无法直接被当作函数调用。

as_view() 的核心使命就是将类转换为函数。来看源码实现:

 复制代码# django/views/generic/base.py@classonlymethod
def as_view(cls, **initkwargs):
    def view(*args, **kwargs):
        self = cls(**initkwargs)   # 每次请求到来时,实例化一个新对象
        return self.dispatch(*args, **kwargs)  # 转交给 dispatch 方法处理
    return view                    # 返回这个内层函数

简化后的逻辑:

 复制代码as_view() 返回一个名为 view 的函数
URL 匹配时调用 view(request)
view 内部 → 实例化当前类 → 调用 dispatch()

值得注意的是,每次请求都会创建全新的类实例,因此完全不必担心多个请求之间的状态相互干扰。


第二个核心问题:dispatch() 如何定位到 get() 方法

这是 CBV 机制中最精华的部分,源码如下:

 复制代码# django/views/generic/base.pydef dispatch(self, request, *args, **kwargs):
    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)

逐行解读:

第一步:将请求方法转换为小写

 复制代码request.method          # 例如 'GET'
request.method.lower()  # 转换为 'get'

第二步:借助 getattr 动态获取对应方法

 复制代码handler = getattr(self, 'get', self.http_method_not_allowed)

这里巧妙利用了 Python 的特性:方法本身就是对象的属性

 复制代码class TestView(APIView):
    def get(self, request):   # 定义了 get 方法
        ...view = TestView()
view.get                      # 可以像访问属性一样获取该方法
getattr(view, 'get')          # 完全等价的写法

因此,getattr(self, 'get', self.http_method_not_allowed) 的含义是:

第三步:执行找到的方法

 复制代码return handler(request, *args, **kwargs)
# 等价于直接调用 self.get(request, *args, **kwargs)

完整请求处理流程

 复制代码客户端发起 GET /api/v1/test/  ↓ URL 路由匹配成功,执行 TestView.as_view() 返回的 view 函数  ↓实例化 TestView,并调用 dispatch(request)  ↓request.method.lower() → 'get'
getattr(self, 'get', http_method_not_allowed) → 成功获取 self.get 方法self.get(request) 被正式调用return Response({"message": "hello"})  ↓客户端收到成功的 HTTP 响应

如果客户端发起了一个 DELETE 请求,但你并未定义 delete() 方法:

 复制代码getattr(self, 'delete', self.http_method_not_allowed)
→ 未找到 self.delete
→ 返回默认值 self.http_method_not_allowed
→ 自动响应 405 Method Not Allowed

框架已经为你做好了兜底处理,无需额外编写判断逻辑。


ViewAPIView 的差异

上文提到的 dispatch 是 Django 原生 View 的核心逻辑。DRF 的 APIView 继承了它,并在 dispatch 中进行了增强:

 复制代码# rest_framework/views.pydef dispatch(self, request, *args, **kwargs):
    # 1. 将原生 request 包装为 DRF Request 对象
    request = self.initialize_request(request, *args, **kwargs)
    
    try:
        # 2. 执行认证、权限、限流等检查
        self.initial(request, *args, **kwargs)
        
        # 3. 与原生 View 一致,根据方法名动态查找并执行
        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:
        # 4. 统一异常处理
        response = self.handle_exception(exc)    return self.finalize_response(request, response, *args, **kwargs)

相比原生 ViewAPIView 额外增加了四层能力:

原生 ViewAPIView
request 对象Django HttpRequestDRF Request(支持 request.data
认证需手动处理自动执行
权限需手动处理自动执行
异常处理需手动处理统一兜底

这也正是开发 DRF 接口时推荐继承 APIView 而非 View 的原因:无需重复处理这些通用逻辑,可以更专注于业务实现。


DRF Request 与原生 Request 的关键区别

APIView 对原生 request 进行了封装,最直观的差异体现在:

 复制代码# 原生 View
request.POST.get('name')   # 仅能获取表单数据
json.loads(request.body)   # JSON 数据需要手动解析# APIView
request.data.get('name')   # 自动解析 JSON / 表单 / multipart 等格式
request.query_params       # 等价于 request.GET,语义更加清晰

总结

阅读完源码后,开头的三个问题都有了明确的答案:

as_view() 的本质是什么? 它将类转换为可调用的函数,使得 URL 路由系统能够正常使用。每次请求进入时都会创建该类的一个新实例。

HTTP 请求是如何找到 get() 方法的? dispatch() 方法通过 getattr(self, request.method.lower(), ...) 动态获取对应方法。在 Python 中,方法本身也是属性,因此 getattr 可以精准定位。

APIView 相比 View 增强了哪些能力? 它封装了 request 对象,自动执行认证、权限和限流检查,并提供统一的异常处理机制。


来源:https://juejin.cn/post/7654139492442079295
上一篇Python环境配置与语法报错排查指南 下一篇Python环境搭建入门指南:从零开始配置到运行第一个程序
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
CentOS与Golang打包常见兼容性问题探讨
编程语言 · 2026-07-01

CentOS与Golang打包常见兼容性问题探讨

CentOS与Golang打包的兼容性问题集中在glibc版本不匹配、交叉编译环境变量错误、依赖库缺失及Go依赖管理不规范。可通过Docker容器编译、选择兼容Go版本、正确设置GOOS GOARCH环境变量、安装对应开发包及使用GoModules解决。

CentOS中Fortran与Python如何协同工作从入门到实战完整教程
编程语言 · 2026-07-01

CentOS中Fortran与Python如何协同工作从入门到实战完整教程

在CentOS中,Fortran与Python可通过f2py、SWIG、共享库调用或subprocess协同。f2py封装Fortran为Python模块,支持数组运算;共享库需手动对齐数据类型;系统调用适合独立计算。

CentOS中Golang打包优化方法
编程语言 · 2026-07-01

CentOS中Golang打包优化方法

在CentOS中优化Golang编译打包,可显著提升编译速度并减小二进制文件体积。关键技巧包括:设置环境变量、使用Go模块管理依赖、编译时添加-ldflags= "-s-w "去除调试信息、利用UPX工具压缩、运行strip清理符号表,以及优化cgo内C代码的编译选项。综合运用这些方法能有效优化最终程序。

在CentOS系统中cpustat与其他工具协同使用的完整方法
编程语言 · 2026-07-01

在CentOS系统中cpustat与其他工具协同使用的完整方法

cpustat作为sysstat包的CPU监控工具,可通过管道与grep等命令配合过滤数据,利用脚本自动记录带时间戳的日志,或结合图形工具查看,也可格式化输出后接入Zabbix、Grafana等Web监控系统,实现可视化与告警。

CentOS中readdir与其他Linux发行版的差异
编程语言 · 2026-07-01

CentOS中readdir与其他Linux发行版的差异

CentOS基于RHEL,与Ubuntu、Debian、Fedora在包管理器(yum dnfvsapt)、默认文件系统(XFSvsext4)等存在差异,但readdir等系统调用遵循POSIX标准,行为一致。