• django之FBV与CBV


    一、FBV

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

    1、在urls.py定义路由

    from django.urls import path
    from app01 import views
    
    urlpatterns = [
        path('user/', views.user), #定义请求路由
    ]

    2、在视图中处理请求

    from django.shortcuts import render,HttpResponse
    
    # Create your views here.
    
    def user(request):
        #处理GET请求
        if request.method == 'GET':
            return HttpResponse('GET')
        
        # 处理POST请求
        if request.method == 'POST':
            return HttpResponse('POST')

    注意:在处理请求时对不同的请求方法进行判断处理的。

    二、CBV

    CBV(class base views) 就是在视图里使用类处理请求,与FBV较大的不同点就是能够根据请求的的方法自己自动执行对应的方法。

    1、在urls.py定义路由

    from django.urls import path
    from app01 import views
    
    urlpatterns = [
    
        path('user/', views.UserView.as_view()),
    
    ]

    2、在视图中处理请求

    from django.shortcuts import render,HttpResponse
    from django.views import View
    
    class UserView(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')
    
        def delete(self,request,*args, **kwargs):
            #删除数据
            return HttpResponse('delete')
    
        def patch(self,request,*args, **kwargs):
            #更新局部数据,例如:用户信息的name这样的某几列
            return HttpResponse('patch')

    在上述CBV中会根据用户请求方法自动去匹配对应的处理方法。其中CBV中提供的接收请求方式有:

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

    3、CBV源码

      CBV中是如何根据用户的请求方法执行对应的方法呢?CBV中使用的就是反射的机制。

      首先,路由中永远都是一个url对应一个函数的形式,所以,可以看到views.UserView.as_view()执行的就是UserView中的as_view方法,如果自己没有就去父类中寻找。也就是

    去View类中寻找。

        def as_view(cls, **initkwargs):
            """Main entry point for a request-response process."""
           
                 ...
    
            def view(request, *args, **kwargs):
                self = cls(**initkwargs)
                if hasattr(self, 'get') and not hasattr(self, 'head'):
                    self.head = self.get
                self.request = request
                self.args = args
                self.kwargs = kwargs
                return self.dispatch(request, *args, **kwargs)
    
             ...
    
            return view                

    可以看到在as_view中返回的是view,所以看看view方法,它返回的就是dispatch方法,这样实际上执行as_view方法得到的实际上就是dispatch方法的返回值。

    在dispatch方法中就进行了请求的分发:

        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) #执行方法,拿到返回值,每个方法返回的结果是什么,就会得到什么

    总结:

    CBV是基于反射实现的,根据不同的请求方式执行不同的方法。
    源码流程:路由 -> as_view方法 -> view方法 -> dispatch方法

    4、扩展

    如果想实现在请求之前做些操作,那么自己可以重写dispatch方法

    class UserView(View):
    
        def dispatch(self, request, *args, **kwargs):
            print('处理方法之前...')
            # 将方法执行的结果返回,两种调用父类的方式
            # ret = super(UserView, self).dispatch(request, *args, **kwargs)
            ret = super().dispatch(request, *args, **kwargs)
            print(ret)
            print('处理方法之后...')
            return ret
    
        def get(self,request,*args, **kwargs):
            #获取数据
            return HttpResponse('GET')
    
        def post(self,request,*args, **kwargs):
            #创建数据
            return HttpResponse('POST')

    执行结果:

    处理方法之前...
    <HttpResponse status_code=200, "text/html; charset=utf-8"> //处理方法返回的结果
    处理方法之后...

    三、FBV与CBV中使用csrf

    django的csrf的实现方式:csrf是基于中间件实现的

    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware', #进行csrf认证的中间件
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]

    并且是在中间件的process_view函数中实现的:

    • 通过检查视图是否免认证(是否存在@csrf_exempt)
    • 从请求体或者cookie中获取csrftoken进行验证,
     def process_view(self, request, callback, callback_args, callback_kwargs):
            if getattr(request, 'csrf_processing_done', False):
                return None
    
            # Wait until request.META["CSRF_COOKIE"] has been manipulated before
            # bailing out, so that get_token still works
            if getattr(callback, 'csrf_exempt', False):
                return None
    
            # Assume that anything not defined as 'safe' by RFC7231 needs protection
            if request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
                if getattr(request, '_dont_enforce_csrf_checks', False):
                    # Mechanism to turn off CSRF checks for test suite.
                    # It comes after the creation of CSRF cookies, so that
                    # everything else continues to work exactly the same
                    # (e.g. cookies are sent, etc.), but before any
                    # branches that call reject().
                    return self._accept(request)
    
                if request.is_secure():
                    # Suppose user visits http://example.com/
                    # An active network attacker (man-in-the-middle, MITM) sends a
                    # POST form that targets https://example.com/detonate-bomb/ and
                    # submits it via JavaScript.
                    #
                    # The attacker will need to provide a CSRF cookie and token, but
                    # that's no problem for a MITM and the session-independent
                    # secret we're using. So the MITM can circumvent the CSRF
                    # protection. This is true for any HTTP connection, but anyone
                    # using HTTPS expects better! For this reason, for
                    # https://example.com/ we need additional protection that treats
                    # http://example.com/ as completely untrusted. Under HTTPS,
                    # Barth et al. found that the Referer header is missing for
                    # same-domain requests in only about 0.2% of cases or less, so
                    # we can use strict Referer checking.
                    referer = request.META.get('HTTP_REFERER')
                    if referer is None:
                        return self._reject(request, REASON_NO_REFERER)
    
                    referer = urlparse(referer)
    
                    # Make sure we have a valid URL for Referer.
                    if '' in (referer.scheme, referer.netloc):
                        return self._reject(request, REASON_MALFORMED_REFERER)
    
                    # Ensure that our Referer is also secure.
                    if referer.scheme != 'https':
                        return self._reject(request, REASON_INSECURE_REFERER)
    
                    # If there isn't a CSRF_COOKIE_DOMAIN, require an exact match
                    # match on host:port. If not, obey the cookie rules (or those
                    # for the session cookie, if CSRF_USE_SESSIONS).
                    good_referer = (
                        settings.SESSION_COOKIE_DOMAIN
                        if settings.CSRF_USE_SESSIONS
                        else settings.CSRF_COOKIE_DOMAIN
                    )
                    if good_referer is not None:
                        server_port = request.get_port()
                        if server_port not in ('443', '80'):
                            good_referer = '%s:%s' % (good_referer, server_port)
                    else:
                        # request.get_host() includes the port.
                        good_referer = request.get_host()
    
                    # Here we generate a list of all acceptable HTTP referers,
                    # including the current host since that has been validated
                    # upstream.
                    good_hosts = list(settings.CSRF_TRUSTED_ORIGINS)
                    good_hosts.append(good_referer)
    
                    if not any(is_same_domain(referer.netloc, host) for host in good_hosts):
                        reason = REASON_BAD_REFERER % referer.geturl()
                        return self._reject(request, reason)
    
                csrf_token = request.META.get('CSRF_COOKIE')
                if csrf_token is None:
                    # No CSRF cookie. For POST requests, we insist on a CSRF cookie,
                    # and in this way we can avoid all CSRF attacks, including login
                    # CSRF.
                    return self._reject(request, REASON_NO_CSRF_COOKIE)
    
                # Check non-cookie token for match.
                request_csrf_token = ""
                if request.method == "POST":
                    try:
                        request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')
                    except IOError:
                        # Handle a broken connection before we've completed reading
                        # the POST data. process_view shouldn't raise any
                        # exceptions, so we'll ignore and serve the user a 403
                        # (assuming they're still listening, which they probably
                        # aren't because of the error).
                        pass
    
                if request_csrf_token == "":
                    # Fall back to X-CSRFToken, to make things easier for AJAX,
                    # and possible for PUT/DELETE.
                    request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, '')
    
                request_csrf_token = _sanitize_token(request_csrf_token)
                if not _compare_salted_tokens(request_csrf_token, csrf_token):
                    return self._reject(request, REASON_BAD_TOKEN)
    
            return self._accept(request)
    process_view

    那么在FBV和CBV中如何进行使用呢?

    1、FBV

    (1)全站使用csrf认证,个别函数免认证

    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware', #全站使用csrf认证
        'django.contrib.auth.middleware.AuthenticationMiddleware', 
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
    

    from django.views.decorators.csrf import csrf_exempt # Create your views here. #该函数无需认证 @csrf_exempt def user(request): #处理GET请求 if request.method == 'GET': return HttpResponse('GET') # 处理POST请求 if request.method == 'POST': return HttpResponse('POST')

    (2)全站不使用csrf认证,个别函数认证

    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        # 'django.middleware.csrf.CsrfViewMiddleware', #全站不进行csrf认证
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
    
    from django.views.decorators.csrf import csrf_exempt,csrf_protect
    
    #该函数认证
    @csrf_protect
    def user(request):
        #处理GET请求
        if request.method == 'GET':
            return HttpResponse('GET')
    
        # 处理POST请求
        if request.method == 'POST':
            return HttpResponse('POST')

    2、CBV

    与FBV有所不同,需要对dispatch函数加上装饰器,当然也是可以进行全站或者局部认证的。

    from django.utils.decorators import method_decorator
    
    @method_decorator(csrf_exempt,name='dispatch') #加上csrf免认证装饰器
    class UserView(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')
    
        def delete(self,request,*args, **kwargs):
            #删除数据
            return HttpResponse('delete')
    
        def patch(self,request,*args, **kwargs):
            #更新局部数据,例如:用户信息的name这样的某几列
            return HttpResponse('patch')

    当然另一种方式是必须写dispatch方法,然后加上装饰器

    from django.utils.decorators import method_decorator
    
    class UserView(View):
    
        @method_decorator(csrf_exempt)#加上csrf免认证装饰器
        def dispatch(self, request, *args, **kwargs):
    
            return super().dispatch(request, *args, **kwargs)
    
        def get(self,request,*args, **kwargs):
            #获取数据
            return HttpResponse('GET')
    
        def post(self,request,*args, **kwargs):
            #创建数据
            return HttpResponse('POST')
  • 相关阅读:
    3里氏代换原则LSP
    2单一职责原则SRP
    1开放封闭原则OCP
    24访问者模式Visitor
    python json模块,处理json文件的读写
    python zip 绑定多个list
    python 字符串重复多次的技巧 *操作符
    python 刷新缓冲区,实时监测
    python os.getcwd 获取工作目录
    python datetime 获取时间
  • 原文地址:https://www.cnblogs.com/shenjianping/p/11379136.html
Copyright © 2020-2023  润新知