当前位置: 首页
编程语言
Django CBV源码解读:请求如何定位get方法

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

热心网友 时间:2026-06-27
转载

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

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

同类文章
更多
AWS RDS 数据库配置入门与基础操作指南

AWS RDS 数据库配置入门与基础操作指南

本文介绍了AWSRDS的基本概念与核心价值,即提供托管式关系数据库服务,简化运维。详细阐述了创建RDS实例的关键配置步骤,包括引擎选择、实例规格、存储与网络设置。最后,指导读者如何通过多种方式安全连接至数据库实例,并开始进行数据操作,为后续应用开发奠定基础。

时间:2026-07-10 06:41
PHP MVC中AJAX请求无法调用控制器方法的原因与解决方案

PHP MVC中AJAX请求无法调用控制器方法的原因与解决方案

PHPMVC中AJAX请求返回整页HTML的常见原因是控制器方法未正确输出响应或未终止执行,导致框架渲染视图。解决方法是在控制器中设置JSON响应头、输出数据后调用exit()明确终止,同时前端使用小写url和dataType: "json "。

时间:2026-07-10 06:40
Go语言手动构造rsa.PublicKey:正确初始化大整数模数N完整指南

Go语言手动构造rsa.PublicKey:正确初始化大整数模数N完整指南

手动构造RSA公钥时,模数N为*big Int类型,不能直接使用超长十进制字面量,需通过SetString或UnmarshalText方法解析字符串。公钥指数E可直接赋值,推荐65537。生产环境应使用rsa GenerateKey生成密钥对,避免手动构造引发的安全和格式错误。

时间:2026-07-10 06:39
Go语言实现HTTP定时轮询监控多URL响应时间与状态检测

Go语言实现HTTP定时轮询监控多URL响应时间与状态检测

使用Go语言实现HTTP定时轮询监控,通过按行分割与Tab解析URL列表,避免闭包陷阱和nil指针,每个URL启动独立ticker安全并发请求,并配置超时控制与资源关闭,确保响应时间与状态码准确检测。

时间:2026-07-10 06:39
Tkinter中Label标签在主循环动态更新的正确方法

Tkinter中Label标签在主循环动态更新的正确方法

在Tkinter中正确动态更新标签的方法:将标签组件的textvariable参数绑定到一个StringVar变量,然后通过调用该变量的 set()方法更新其值,界面会自动刷新。这样避免直接修改text属性或调用update()。此做法实现数据与界面的解耦,代码更简洁,响应更及时,避免手动同步的闪烁,推荐做法。

时间:2026-07-10 06:39
热门专题
更多
刀塔传奇破解版无限钻石下载大全 刀塔传奇破解版无限钻石下载大全
洛克王国正式正版手游下载安装大全 洛克王国正式正版手游下载安装大全
思美人手游下载专区 思美人手游下载专区
好玩的阿拉德之怒游戏下载合集 好玩的阿拉德之怒游戏下载合集
不思议迷宫手游下载合集 不思议迷宫手游下载合集
百宝袋汉化组游戏最新合集 百宝袋汉化组游戏最新合集
jsk游戏合集30款游戏大全 jsk游戏合集30款游戏大全
宾果消消消原版下载大全 宾果消消消原版下载大全
  • 热门数据榜