zoukankan      html  css  js  c++  java
  • 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')
  • 相关阅读:
    MFC tab页面中获到其它页面的数据
    sqlite数据库中"Select * From XXX能查到数据,但是Select DISTINCT group From xxx Order By group却查不出来
    关闭程序出现崩溃(exe 已触发了一个断点及未加载ucrtbased.pdb)
    springboot 通用Mapper使用
    springBoot 发布war包
    springCloud Zuul网关
    springboot hystrix turbine 聚合监控
    springBoot Feign Hystrix Dashboard
    springBoot Ribbon Hystrix Dashboard
    springBoot Feign Hystrix
  • 原文地址:https://www.cnblogs.com/shenjianping/p/11379136.html
Copyright © 2011-2022 走看看