python3-開發進階Django-CBV和FBV及CBV的源碼分析

1、CBV和FBV

全稱應該是class base views 和function base views
理解起來應該就是基於類的視圖函數和基於函數的視圖函數html

FBV

應該是我目前最經常使用的一種方式了,就是給每個views裏的功能添加本身專用的方法。例如若是要對網頁進行get訪問,而後經過得到request中post方式傳遞的form表單獲取數據。shell

from django.http import HttpResponse
  
def login(request):
    if request.method == 'GET':
        return HttpResponse('OK1')

    if request.method =="POST":
         return HttpResponse('OK2')
   。。。

CBV

可是這種方法,看起來有點臃腫,查看代碼的時候不容易看清楚你的post請求get請求是在哪裏處理的,因此就有了CBV的處理方法。django

在views文件中:app

from django.views import View
class LoginView(View):
    def get(self,request):
        return render(request,"login.html")
    def post(self,request):
        user=request.POST.get('user')
        pwd=request.POST.get('pwd'))
        if Turn:  #假使驗證成立
            return HttpResponse("OK3")

在CBV的使用中,須要調用父類View,它會在源碼裏解釋這個CBV的應用範圍,以及運做原理。函數

在urls文件中:post

from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/$',views.LoginView.as_view()),
  # url(r'^login/$',views.login) ]

綜上所述:
咱們能夠知道設置一個CBV方法,要作的就是,在views裏建立一個類,這個類的父類必定得是View,並且在urls設置的時候,url指向的再也不是一個函數名,而是你定義的類的.as_view()方法url

運行起來以後,會發現當向login.html這個url發送get請求的時候,成功,發送post請求的時候,也成功,而且有相應的返回值。spa

2、原理

翻看源碼,實現以上功能的核心,其實都在那個被繼承的父類View裏:code

class View(object):
    """
    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 six.iteritems(kwargs):
            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.request = request
            self.args = args
            self.kwargs = kwargs
            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 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 http.HttpResponseNotAllowed(self._allowed_methods())

    def options(self, request, *args, **kwargs):
        """
        Handles responding to requests for the OPTIONS HTTP verb.
        """
        response = http.HttpResponse()
        response['Allow'] = ', '.join(self._allowed_methods())
        response['Content-Length'] = '0'
        return response

    def _allowed_methods(self):
        return [m.upper() for m in self.http_method_names if hasattr(self, m)]

從源碼中咱們能夠看到。這個CBV的核心類,是爲了處理各類請求服務的。其中有一個list來存放這些請求,而且指向他們應該實現的函數功能。
再補充一點,在CBV建立類方法的時候,必定要攜帶一個request參數。而這個參數裏面就攜帶了request.method.lower(),經過反射,CBV函數天然能處理這些method對應的請求。orm

3、邏輯過程

看源碼的準則就看,看本身看的懂得代碼,應爲源碼的有些高深的設置,咱們先階段還不須要去了解.

首先,無論cbv仍是fbv,在url中都是用戶訪問請求,纔回去執行相應的視圖函數,

cbv在啓動項目的時候,已經在LoginView中執行一段騷操做的獲得一個函數名,咱們如今就來看看:

首先咱們要明確的一點就是一個類.屬性或方法,首先在本身那找,找不到繼承的父類找。因此不能直接點。as_view,

先去類LoginView中找看看有,有沒有方法:

點開View,咱們找到as_view方法,發現是類方法,

前面過程作了什麼咱們無論,返回值只是返回一個view,調用view函數,咱們去看view函數,

view函數最後返回self.dispacth,這裏要注意!!!這個self.dispacth,self是誰,self咱們看代碼發現是LoginView類,

仍是那個準則,調用屬性方法,如今本身那邊找,沒有再找父類。

由於LoginView中沒有dispath這方法,因此仍是執行的父類的dispath方法:

注意點:仍是在調用某個方法時,咱們必定要肯定是誰去調用這個方法!找方法必定先找本身的,沒有再去父類找!

4、思考自定義的dispatch

既然在上面咱們查看源碼的時候已經發現,導向專門的method操做的函數是dispatch,並且每一個CBV類的父類都是View,那我能不能在這個dispatch裏面作一些定製化操做,

  • 繼承父類的dispatch

     def dispatch(self, request, *args, **kwargs):
        obj=super(login,self).dispatch(request, *args, **kwargs)
        return obj
        # 因爲父類的dispatch最後返回了一個handle,也就是一個返回值,因此在繼承的時候也應該提供一個返回值
  • 裝飾化這個dispatch

    def dispatch(self, request, *args, **kwargs):
        print('123‘)
        obj=super(login,self).dispatch(request, *args, **kwargs)
        print('456')
        return obj

    反饋的效果:

    不論是什麼方法請求,都必需要打印123,456

總的來講,這個只是一個簡單的示範處理,若是須要對過來的請求作更多的潤色,還須要在這個繼承動做先後作更多工做。須要知道的是,他和裝飾器略微不懂,那就是他能夠共享這個dispatch的request,而且對他進行工做

相關文章
相關標籤/搜索