首页 > 编程语言 >Django CBV源码解读:请求如何调用get()方法

Django CBV源码解读:请求如何调用get()方法

来源:互联网 2026-06-27 07:56:01

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

Django CBV 源码解读:一个请求是怎么找到你的 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
  • 请求进入后,如何自动定位到 get() 方法?
  • 如果定义了 post(),POST 请求又怎样知道去调用它?

带着这些疑问翻阅源码后,所有疑惑便迎刃而解。


FBV vs 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 == 语句,每种请求方法对应一个函数,职责明确
  • 可通过继承复用,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"})  ↓客户端收到响应

如果客户端发送了 DELETE 请求,而你没有定义 delete() 方法:

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

框架已为你兜底,无需自行处理。


View vs APIView

以上所述的 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)

与原生 View 相比,APIView 额外做了四件事情:

原生 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 路由系统能够使用。每次请求到来时都会实例化一个新对象。

请求如何找到 get() 方法? dispatch() 通过 getattr(self, request.method.lower(), ...) 动态获取方法。在 Python 中,方法本质也是属性,因此 getattr 能够取到。

APIViewView 多了哪些功能? 包装了 request 对象,自动执行认证/权限/限流,并统一处理异常。


侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述

热游推荐

更多
湘ICP备14008430号-1 湘公网安备 43070302000280号
All Rights Reserved
本站为非盈利网站,不接受任何广告。本站所有软件,都由网友
上传,如有侵犯你的版权,请发邮件给xiayx666@163.com
抵制不良色情、反动、暴力游戏。注意自我保护,谨防受骗上当。
适度游戏益脑,沉迷游戏伤身。合理安排时间,享受健康生活。