访问流程源码剖析,这里我们主要看用户的请求是如何访问到我们的视图函数。
注意:一定要跟着博主的解说再看代码中文注释及其下面的代码!!!
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={"code": 200, "result": res})GroupsView