2.CBV和类视图as_view源码解析

FBV和CBV#

FBV(视图基于函数开发): function、base、views
CBV(视图基于类开发): class 、base 、views

Python是一个面向对象的编程语言,如果只用函数来开发,有很多面向对象的优点就错失了(继承、封装、多态)。Django在后来加入了Class-Based-View。可以让我们用类写View。这样做的优点主要下面两种:

  • 提高了代码的复用性,可以使用面向对象的技术,比如Mixin(多继承)
  • 可以用不同的函数针对不同的HTTP方法处理,而不是通过很多if判断,提高代码可读性
  • fbv和cbv本质上是一样的,一个是基于函数开发,一个是基于类开发
  • CBV基于反射实现根据请求方式的不同,执行不同的方法

类视图#

类视图必须继承自View(Django原生视图)
from django.views import View

Django会根据发送过来的请求方式,执行对应的请求
相当于视图类里面给我们做了一个分发

class Infos(View):
    #例如客户端发送get请求,则会触发get函数下的内容
    def get(self,request,*args,**kwargs):
        pass
    #例如客户端发送post请求,则会触发get函数下的内容    
    def post(self,request,*args,**kwargs):
        pass
    #例如客户端发送put请求,则会触发get函数下的内容    
    def put(self,request,*args,**kwargs):
        pass
    #例如客户端发送的delete请求,则会触发get函数下的内容    
    def delete(self,request,*args,**kwargs):
        pass


2.路由中的视图指向类后面需要加as_view


from . import views
urlpatterns = [
    path('admin/', admin.site.urls),
    # 路由地址 / views.类名.as_view
    path('infos/', views.Infos.as_view()),
]
 

as_view源码解析#

as_view()核心流程

  • as_view() 内部定义了 view() 函数。view() 函数对类视图进行初始化,返回并调用了 dispatch() 方法。

  • dispatch() 根据请求类型的不同,调用不同的函数(如 get() 、 post()),并将这些函数的 response 响应结果返回。

  • as_view() 返回了这个 view 函数闭包,供 path() 路由调用

原理:url - > view方法 - > dispatch方法(反射执行其他:GET/POST/DELETE/PUT...)
基于反射实现根据请求方式的不同,执行不同的方法

源码

    '''
    as_view() 是View的类方法
    进入 as_view() 后循环对传入的参数做简单的校验,避免传入的参数将类自己的关键函数名覆盖掉或者传入类中没定义的属性
    as_view() 内部又定义了一个 view() 函数,
    view()首先实例化了类自己 cls(),并赋值给 self ,也就是你编写的类视图的实例。
    接着调用 self.setup() 对实例的属性进行了初始化。setup() 方法把接收的参数原封不动的赋值到类实例中。
    view() 函数最后返回了 dispatch()
    dispatch() 非常简短,功能却非常重要:如果 request.method 是一个 GET 请求,则调用类视图 self.get() 方法,如果是 POST 请求,那就调用 self.post() 方法。这就起到根据 http 请求类型派发到不同函数的功能
    回到 as_view() 来,它最后做了属性赋值、修改函数签名等收尾工作后,返回了 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

        # take name and docstring from class
        update_wrapper(view, cls, updated=())

        # and possible attributes set by decorators
        # like csrf_exempt from dispatch
        update_wrapper(view, cls.dispatch, assigned=())
        return view

    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

    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)

    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}
        )
        return HttpResponseNotAllowed(self._allowed_methods())

作者:木子七

出处:https://www.cnblogs.com/Mickey-7/p/15798601.html

posted @   木子七  阅读(103)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示
workspaces
keyboard_arrow_up dark_mode palette
选择主题
menu