Django restframework as_view()解析

一、FBV和CBV

在开始讲as_view()函数前,先来聊一聊django里两种不同的视图模式,他们分别是FBV和CBV。

1、FBV:基于函数的视图

urlpatterns = [
    path('demo/', views.demo),
    path('admin/', admin.site.urls),
]
def demo(request):
    return HttpResponse('demo')

在基于函数的视图里,一个URL对应一个视图函数,优点是简单易懂,缺点是难以复用。

2、FCB:基于类的视图

urlpatterns = [
    path('DemoView/', views.DemoView.as_view()),
]
class DemoView(View):
    def get(self, request):
        return HttpResponse('get')

在FCB模式中,视图是基于自定义类的,但类必须继承自View类。而as_view()方法是View中的一个类方法,它会根据请求的Method方法自动调用相应的函数进行处理。

二、手动实现as_view()方法

as_view() 的核心流程:

  • as_view() 内部定义了 view() 函数。view() 函数对类视图进行初始化,调用并返回了 dispatch() 方法。
  • dispatch() 根据请求类型的不同,调用不同的函数(如 get() 、 post()),并将这些函数的 response 响应结果返回。
  • as_view() 返回了这个 view 函数闭包,供 path() 路由调用。
class DemoView():
    @classmethod
    def as_view(cls, **initkwargs):
        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            self.request = request
            self.args = args
            self.kwargs = kwargs
            return self.dispatch(request, *args, **kwargs)
        return view
    
    def dispatch(self, request, *args, **kwargs):
        handler = getattr(
            self, request.method.lower()
        )
        return handler(request, *args, **kwargs)

    def get(self, request):
        return HttpResponse('get')

as_view方法在Django启动时就会被调用,那么对应的URL实际上就和view方法进行了绑定,每一次对该URL的请求都会调用view方法。

三、as_view()源码解析

class DemoView(View):
    def get(self, request):
        return HttpResponse('get')

在View类中定义了as_view()方法,下面是View中的as_view()方法。

@classonlymethod
    def as_view(cls, **initkwargs):
        """Main entry point for a request-response process."""
        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)
                )

        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            self.setup(request, *args, **kwargs)
            if not hasattr(self, "request"):
                raise AttributeError(
                    "%s instance has no 'request' attribute. Did you override "
                    "setup() and forget to call super()?" % cls.__name__
                )
            return self.dispatch(request, *args, **kwargs)

        view.view_class = cls
        view.view_initkwargs = initkwargs

        # __name__ and __qualname__ are intentionally left unchanged as
        # view_class should be used to robustly determine the name of the view
        # instead.
        view.__doc__ = cls.__doc__
        view.__module__ = cls.__module__
        view.__annotations__ = cls.dispatch.__annotations__
        # Copy possible attributes set by decorators, e.g. @csrf_exempt, from
        # the dispatch method.
        view.__dict__.update(cls.dispatch.__dict__)

        # Mark the callback if the view class is async.
        if cls.view_is_async:
            view._is_coroutine = asyncio.coroutines._is_coroutine

        return view

在View类的as_view()方法中先是对参数进行校验,先是校验接收到的请求中的method是不是合法,再是校验对应的方法是否已实现,接下来定义view方法,最后返回view,这样每一次的URL请求就会由view进行处理,而view接收的参数和普通的函数视图是一样的,也是包括request对象以及从url获取的args和kwargs参数。

在view方法中先是调用cls(**initkwargs)实例化类,并赋值给self,也就是自己编写的类视图的实例。

    def setup(self, request, *args, **kwargs):
        """Initialize attributes shared by all view methods."""
        if hasattr(self, "get") and not hasattr(self, "head"):
            self.head = self.get
        self.request = request
        self.args = args
        self.kwargs = kwargs

接着调用self.setup(request, *args, **kwargs)对实例的属性进行初始化,它简单地将接收到的参数原封不动的赋值到类实例中。

    def dispatch(self, request, *args, **kwargs):
        # Try to dispatch to the right method; if a method doesn't exist,
        # defer to the error handler. Also defer to the error handler if the
        # request method isn't on the approved list.
        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)

view方法最后调用了dispatch()方法,dispatch()非常简短,但它是最核心的地方,先是获取请求中的method,再利用反射调用类视图中相应的方法。

回到as_view(),它最后做了属性赋值、修改函数签名等收尾工作后,返回了view函数闭包。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值