訪問流程原始碼剖析,這里我們主要看用戶的請求是如何訪問到我們的視圖函式,
注意:一定要跟著博主的解說再看代碼中文注釋及其下面的代碼!!!
1、運行專案命令:python manage.py runserver 這里就可以看出程式的入口時manage.py檔案
#!/usr/bin/env python """Django's command-line utility for administrative tasks.""" import os import sys def main(): # 加載專案的全域組態檔 os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mySwagger.settings') try: from django.core.management import execute_from_command_line except ImportError as exc: raise ImportError( "Couldn't import Django. Are you sure it's installed and " "available on your PYTHONPATH environment variable? Did you " "forget to activate a virtual environment?" ) from exc execute_from_command_line(sys.argv) if __name__ == '__main__': main()專案的manage.py
2、程式中就是加載專案的全域組態檔,這里是部分全域配置代碼,
import os import sys # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 將創建的APP放置到apps根目錄下,易管理 sys.path.insert(0,os.path.join(BASE_DIR, 'apps')) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = '+6mgo4jn^z8&ydx&@n8q++3x1u3mym+)@yt#fah=qbxyl*wat+' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = ['*'] # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', # 內置APP "rest_framework", # 自己創建的APP "words", ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', # cors請求中間件 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ROOT_URLCONF = 'mySwagger.urls' WSGI_APPLICATION = 'mySwagger.wsgi.application'mySwagger.settings.py
3、然后然后運行最后一句代碼,application也就正式啟動并且就是等待用戶的請求,
4、用戶這時發出一個http請求(get、post、put、delete、patch、options、head、trace等八種其中一種),先進入全域的組態檔中從上到下依次執行:
①app目錄(就是放置一個個application)已經創建好
②application添加到了INSTALL_APPS串列中(有內置的,也有自己的)
③再執行到Django的中間件,這里先補充一個知識點
Django中間件最多只能定義五個方法:process_request, process_view, process_response, process_exception, process_template_response.
Django中間的執行流程和用戶的請求有什么關系呢,用戶發出請求是先要執行所有Django的中間件的process_request方法如圖所示,最后根據用戶請求的路由拆分先去主配置路由匹配,去到確定的application的路由中再去匹配即將要執行的視圖函式,但是沒有立刻執行,而是再去執行所有的Django中間件的process_view方法,這個方法很重要,下面我會專門介紹process_view的一個用處,執行完之后就執行根據路由匹配的視圖的方法,最后根據方法的回傳值的形式執行所有的Django中間件的其他三種方法,

5、這里我就補充上述的process_view方法的重要示例:csrftoken Django中間件(Django自帶的中間件)的實作,它的內部實作就是在process_view方法檢驗根據路由匹配的視圖方法中是否添加了免除驗證的裝飾器,在Django框架中是有兩種視圖形式:FBV(Function Basic View)和CBV(Class Basic View),所以在實際開發中需要認證和免除認證的兩種裝飾器在這兩種視圖形式使用也是有不同之處的,
①在FBV中,使用裝飾器(csrf_exempt免除認證,csrf_protect需要認證)需要匯入:from django.views.decorators.csrf import csrf_exempt, csrf_protect,我們只需要在方法頭上添加這兩種注解,
②在CBV中,不是直接使用上面這兩種注解,他另外還需要匯入一個東西:from django.utils.decorators import method_decorator,注解方式有兩種,第一種我們直接在類上添加@method_decorator(),第一個引數就是上述兩個注解的一個,第二個引數name指定dispatch方法名,第二種就是我們在類中自定義一個dispatch方法,在方法上添加@method_decorator(注解上一個),
接下來終于回歸到了正題
6、第5中出現了FBV、CBV、dispatch方法,FBV就是基于函式的視圖,是根據路由直接匹配到視圖函式,然后視圖函式給定一個回傳值,這個沒有更好的封裝,面向物件,就不建議使用,CBV就是基于類的視圖,是根據路由匹配到視圖類,事先執行視圖類的as_view方法
from django.conf.urls import url from . import views app_name = '[words]' urlpatterns = [ url(r'groupsSelectAll/', views.GroupsView.as_view(), name="groupsSelectAll"), # 詞組資訊查詢所有 ]
7、但是GroupsView類下沒有as_view方法,這時就要去它的父類APIView查看(點進去看as_view方法),這里博主只復制方法源代碼,大家只需要看中文注釋及其下的代碼陳述句,在這個方法中值得一提的是super關鍵字,如果請求視圖類(就是GroupsView類,如果繼承了多個父類)還有另一個父類,它先會查看這個父類是否有as_view方法,在這里它是會執行APIView的父類View中的as_view方法,然后我們再次查看父類View的as_view方法,第一個as_view方法是APIView類的,第二個as_view方法是View類的,
@classmethod def as_view(cls, **initkwargs): """ Store the original class on the view function. This allows us to discover information about the view when we do URL reverse lookups. Used for breadcrumb generation. """ if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet): def force_evaluation(): raise RuntimeError( 'Do not evaluate the `.queryset` attribute directly, ' 'as the result will be cached and reused between requests. ' 'Use `.all()` or call `.get_queryset()` instead.' ) cls.queryset._fetch_all = force_evaluation # 執行父類的as_view方法 view = super(APIView, cls).as_view(**initkwargs) view.cls = cls view.initkwargs = initkwargs # Note: session based authentication is explicitly CSRF validated, # all other authentication is CSRF exempt. return csrf_exempt(view)APIView.as_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("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)) # 執行view方法 def view(request, *args, **kwargs): # 這里的cls就是我們的請求視圖類,顯而易見,這個self是請求試圖類的物件 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 # 然后這里就是執行dispatch方法 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 viewView.as_view()
8、我們在第二個as_view方法中可以知道self是我們的請求視圖類的物件,通過這個self呼叫dispatch方法,請求視圖類中沒有dispatch方法,是不是又去APIView類中執行dispatch方法,這里就是上面提到的dispatch方法,這個方法里面有一行代碼handler = getattr(self, request.method.lower(), self.http_method_not_allowed),小小的一行代碼很精髓,這里使用了python的反射機制,根據博主再代碼中的注釋中這個self是請求視圖類(GroupsView),request.method.lower()就是上述說的用戶的八種請求方式之一,如果沒有,就執行第三個引數的方法,所以我們需要再請求視圖類上必須寫上對應的方法,這樣python的反射機制才能反射執行到請求視圖類我們定義的方法
def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """ self.args = args self.kwargs = kwargs # 這里是對原生的request加工處理,回傳一個新的request物件 request = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers # deprecate? try: # 初始化(用戶登錄認證,權限驗證,訪問頻率限制) self.initial(request, *args, **kwargs) # Get the appropriate handler method if request.method.lower() in self.http_method_names: # 通過python的反射機制反射到請求視圖類的方法名稱 handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed # 最后就是執行請求視圖類的方法 response = handler(request, *args, **kwargs) except Exception as exc: response = self.handle_exception(exc) self.response = self.finalize_response(request, response, *args, **kwargs) return self.responseAPIView.dispatch()
9、最后執行請求視圖類的get方法(假設用戶發的get請求)
class GroupsView(APIView): def get(self, request): conditions = { "id": request.query_params.get("wid"), "name": request.query_params.get("name"), "start_time": request.query_params.get("start_time"), "end_time": request.query_params.get("end_time"), } res = DataManager.select_by_conditions("words_groups", None, **conditions) return Response(data=https://www.cnblogs.com/aitiknowledge/p/{"code": 200, "result": res})GroupsView
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/140868.html
標籤:Python
