Django系列之FBV和CBV-04

1. 前言

在之前django的学习中,有两个术语FBV和CBV。

FBV(function base views) 就是在视图里使用函数处理请求。
CBV(class base views) 就是在视图里使用类处理请求。

2. FBV

FBV就是我们常用的视图函数。通过URLconf(urls.py)找到对应的视图函数。

3. CBV

本文着重讲CBV

3.1 CBV和FBV用法的不同

首先,讲讲CBV相对于FBV有什么优势吧。

视图类相对于视图函数的优势就是更灵活、更易扩展。因为类可以继承啊。

Django官网例子中视图函数如下:

# views.py
from django.http import HttpResponse

def my_view(request):
    if request.method == 'GET':
        # <view logic>
        return HttpResponse('result')

Django官网例子中视图类如下:

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

class MyView(View):
    def get(self, request): # 名字对应的HTTP的请求方法,get、post、put patch、delete、 head、 options、 trace。
        # <view logic>
        return HttpResponse('result')

视图类对应的URLconf中的内容:

# urls.py
from django.urls import path
from myapp.views import MyView

urlpatterns = [
    path('about/', MyView.as_view()),
]

3.2 视图的逻辑

在上面的URLconf中视图类的as_view()方法返回的是一个视图函数。

django.views.View类的源码如下:


class View:
    """
    Intentionally simple parent class for all views. Only implements
    dispatch-by-method and simple sanity checking.
    """

    http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

    def __init__(self, **kwargs):
        """
        Constructor. Called in the URLconf; can contain helpful extra
        keyword arguments, and other things.
        """
        # Go through keyword arguments, and either save their values to our
        # instance, or raise an error.
        for key, value in kwargs.items():
            setattr(self, key, value)

    @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("You tried to pass in the %s method name as a "
                                "keyword argument to %s(). Don't do that."
                                % (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)
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            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."""
        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:
        	# 根据request请求的方法将此请求分派给对应的实例方法处理。
            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())
        

as_view()方法调用了setup()方法和dispatch()方法。

setup()方法是用来初始化参数的。

dispatch()方法是根据request请求的方法来讲这个URL分派给对应的方法。例如:request.method是’GET’,则将此请求交由def get(self, request)处理。所以在继承的子类视图中需要我们自己定义对应的get()实例方法、post()实例方法等。

4. 装饰视图类

4.1 在URLconf中装饰

装饰基于类的视图最简单的方式是装饰 as_view()方法的结果。

官方示例:

# urls.py


from django.contrib.auth.decorators import login_required, permission_required
from django.views.generic import TemplateView

from .views import VoteView

urlpatterns = [
    path('about/', login_required(TemplateView.as_view(template_name="secret.html"))),
    path('vote/', permission_required('polls.can_vote')(VoteView.as_view())),
]

这种方法并不能装饰该视图类的每一个实例,只有匹配该URL才能装饰该视图类的as_view()类方法的返回值。

4.2 直接装饰视图类

通过这种方式,能够装饰该视图类的每一个实例。

官方示例:

from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.generic import TemplateView

class ProtectedView(TemplateView):
    template_name = 'secret.html'

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        return super().dispatch(*args, **kwargs)

下面这种方式等价:

@method_decorator(login_required, name='dispatch')
class ProtectedView(TemplateView):
    template_name = 'secret.html'

需要使用到多个装饰器时,有如下两种方式:

decorators = [never_cache, login_required]

@method_decorator(decorators, name='dispatch')
class ProtectedView(TemplateView):
    template_name = 'secret.html'

@method_decorator(never_cache, name='dispatch')
@method_decorator(login_required, name='dispatch')
class ProtectedView(TemplateView):
    template_name = 'secret.html'

需要注意的是装饰的顺序时先never_cache,再login_required

5. 总结

使用CBV要比FBV更好,FBV能做的事,CBV都能做,但CBV的扩展性要比FBV强的多。

6. 参考文献

[1] 【Django】Django架构流程分析
[2] Django源码
[3] 廖雪峰官方网站

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值