DjangoCBV中,`as_view()`将类转换为函数供路由调用,每次请求实例化新对象。`dispatch()`通过`getattr`根据请求方法名动态调用对应方法,未定义则返回405。`APIView`在此基础上包装请求对象,自动执行认证、权限、限流和异常处理。
对于从前端转行学习 Django 的开发者来说,首次看到这种写法时可能会感到困惑:

长期稳定更新的攒劲资源: >>>点此立即查看<<<
复制代码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 请求又怎样知道去调用它?带着这些疑问翻阅源码后,所有疑惑便迎刃而解。
首先了解背景。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 额外做了四件事情:
| 原生 View | APIView | |
|---|---|---|
| request 对象 | Django HttpRequest | DRF Request(支持 request.data) |
| 认证 | 手动处理 | 自动执行 |
| 权限 | 手动处理 | 自动执行 |
| 异常处理 | 手动处理 | 统一兜底 |
这也是编写 DRF 接口时应继承 APIView 而非 View 的原因:无需自行处理这些通用逻辑,只需专注于业务开发。
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 能够取到。
APIView 比 View 多了哪些功能?
包装了 request 对象,自动执行认证/权限/限流,并统一处理异常。
侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述