00:00

文章目录

加载目录中...

Django的View类源码分析

我们来深入解析 Django 中 View 方法的源码,特别是它的方法分发机制。这是理解 Django 基于类的视图 (Class-Based Views, CBV) 工作原理的核心。

1. 核心类:View

所有 Django 的基于类的视图都继承自 django.views.generic.base.View。这是整个 CBV 体系的基石。

位置: django/views/generic/base.py

2. 入口点:as_view 类方法

当我们这样定义 URL 时:

urlpatterns = [
    path('my-url/', MyView.as_view(), name='my-view'),
]

as_view() 是一个关键的类方法,它负责创建视图的可调用入口。当请求来时,调用url对应的类方法as_view(),我们在视图函数定义的类中没有as_view()方法,所以会在它继承的View父类中找,找到后执行父类中的as_view()方法,在这里的as_view()函数做了一个闭包,里面套着一个view()函数,as_view()方法返回了view()函数。所以请求来时调用as_view()方法返回的是view()函数。在view()函数中调用了dispatch方法进行分发。后面将会介绍到dispatch方法。

class View:
    # ... 其他代码 ...
    
    @classonlymethod
    def as_view(cls, **initkwargs):
        """主入口点,返回一个视图函数。"""
        # 1. 验证传入的关键字参数是否合法
        for key in initkwargs:
            if key in cls.http_method_names:
                raise TypeError(
                    "The method name %s is not accepted as a keyword argument "
                    "to %s()." % (key, cls.__name__)
                )
            if not hasattr(cls, key):
                raise TypeError(
                    "%s() received an invalid keyword %r. as_view only "
                    "accepts arguments that are already attributes of the class."
                    % (cls.__name__, key)
                )

        # 2. 定义内部的视图函数
        def view(request, *args, **kwargs):
            # 3. 创建视图类的实例
            self = cls(**initkwargs)
            # 4. 设置实例属性
            self.setup(request, *args, **kwargs)
            # 5. 如果没有request对象,抛出错误
            if not hasattr(self, 'request'):
                raise AttributeError(
                    "%s instance has no 'request' attribute. Did you override "
                    "setup() and forget to call super()?" % cls.__name__
                )
            # 6. 调用dispatch方法进行方法分发!
            return self.dispatch(request, *args, **kwargs)
        
        # 7. 给返回的函数添加一些属性,便于调试和识别
        view.view_class = cls
        view.view_initkwargs = initkwargs
        
        # 8. 复制类的一些重要属性到函数上
        update_wrapper(view, cls, updated=())
        update_wrapper(view, cls.dispatch, assigned=())
        
        return view

3. 方法分发的核心:dispatch 方法

dispatch 方法是真正实现"根据 HTTP 方法调用不同处理函数"的地方。

class View:
    # 支持的HTTP方法列表
    http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
    
    def dispatch(self, request, *args, **kwargs):
        # 1. 检查请求方法是否在允许的列表中
        if request.method.lower() in self.http_method_names:
            # 2. 使用getattr动态获取对应的处理方法
            # 例如:对于GET请求,获取self.get方法
            # 对于POST请求,获取self.post方法
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        else:
            # 3. 如果是不支持的HTTP方法,使用http_method_not_allowed
            handler = self.http_method_not_allowed
        
        # 4. 调用获取到的处理方法,并返回结果
        return handler(request, *args, **kwargs)

4. 辅助方法

http_method_not_allowed: 当请求使用了不被支持的 HTTP 方法时调用。

def http_method_not_allowed(self, request, *args, **kwargs):
    # 记录警告日志
    logger.warning(
        'Method Not Allowed (%s): %s', request.method, request.path,
        extra={'status_code': 405, 'request': request}
    )
    # 返回405 Method Not Allowed响应
    return HttpResponseNotAllowed(self._allowed_methods())

_allowed_methods: 返回视图支持的 HTTP 方法列表。

def _allowed_methods(self):
    return [m.upper() for m in self.http_method_names if hasattr(self, m)]

setup (Django 3.2+): 初始化视图实例的属性。

def setup(self, request, *args, **kwargs):
    """初始化视图实例的通用属性。"""
    self.request = request  # Django的HttpRequest对象
    self.args = args        # URL捕获的位置参数
    self.kwargs = kwargs    # URL捕获的关键字参数

5. 完整的工作流程

  1. URL 匹配:Django 收到请求,匹配到 path('my-url/', MyView.as_view(), ...)
  2. 创建视图函数as_view() 被调用,返回内部的 view 函数
  3. 调用视图函数:Django 调用 view(request, *args, **kwargs)
  4. 实例化视图类:在 view 函数内部创建 MyView 的实例
  5. 设置属性:调用 setup() 设置 request, args, kwargs
  6. 方法分发:调用 dispatch() 方法
  7. 获取处理函数dispatch() 根据请求方法找到对应的处理函数(如 get(), post()
  8. 执行业务逻辑:调用具体的处理函数
  9. 返回响应:处理函数返回 HttpResponse 对象

6. 一个简单的示例

from django.views import View
from django.http import HttpResponse

class MyView(View):
    def get(self, request, *args, **kwargs):
        return HttpResponse("这是GET请求的响应")
    
    def post(self, request, *args, **kwargs):
        return HttpResponse("这是POST请求的响应")
    
    def put(self, request, *args, **kwargs):
        return HttpResponse("这是PUT请求的响应")

7. 源码设计的优点

  1. 简洁清晰:整个分发机制非常简洁,只有几十行代码
  2. 可扩展性:可以通过重写 dispatch 方法来自定义分发逻辑
  3. 一致性:所有基于类的视图都遵循相同的模式
  4. 灵活性:支持所有标准的 HTTP 方法
返回文章列表

评论区 0