django源码分析

本文环境python3.5.2,django1.10.x系列

根据前上一篇runserver的博文,已经分析了本地调试服务器的大致流程,现在我们来分析一下当runserver运行起来后,django框架是如何处理一个请求的,django框架是遵循了wsgi标准,所以django的项目可以和gunicorn等wsgi服务器配合使用,此处我们就主要分析一下django的wsgi流程分析。
在runserver函数中,有调用

def get_internal_wsgi_application():"""Loads and returns the WSGI application as configured by the user in``settings.WSGI_APPLICATION``. With the default ``startproject`` layout,this will be the ``application`` object in ``projectname/wsgi.py``.This function, and the ``WSGI_APPLICATION`` setting itself, are only usefulfor Django's internal server (runserver); external WSGI servers should justbe configured to point to the correct application object directly.If settings.WSGI_APPLICATION is not set (is ``None``), we just returnwhatever ``django.core.wsgi.get_wsgi_application`` returns."""from django.conf import settings                                                # 导入配置文件app_path = getattr(settings, 'WSGI_APPLICATION')                                # 获取配置文件中的wsgi运行的路径if app_path is None:                                                            # 如果配置文件中没有则django/core/wsgi中的WSGIHandlerreturn get_wsgi_application()try:return import_string(app_path)                                              # 如果配置文件中配置,则使用配置文件中的包except ImportError as e:msg = ("WSGI application '%(app_path)s' could not be loaded; ""Error importing module: '%(exception)s'" % ({'app_path': app_path,'exception': e,}))six.reraise(ImproperlyConfigured, ImproperlyConfigured(msg),sys.exc_info()[2])

其中,函数get_wsgi_application()就是调用Django框架的wsgi来处理请求,
该函数位于django/core/wsgi.py中

def get_wsgi_application():"""The public interface to Django's WSGI support. Should return a WSGIcallable.Allows us to avoid making django.core.handlers.WSGIHandler public API, incase the internal WSGI implementation changes or moves in the future."""django.setup(set_prefix=False)                                      # 初始化django环境return WSGIHandler()    

其中django.setup()函数,会在后续文章中详细分析,Django环境的初始化,现在我们直接分析WSGIHandler类:

class WSGIHandler(base.BaseHandler):request_class = WSGIRequestdef __init__(self, *args, **kwargs):super(WSGIHandler, self).__init__(*args, **kwargs)self.load_middleware()                                                              # 加载中间件def __call__(self, environ, start_response):set_script_prefix(get_script_name(environ))signals.request_started.send(sender=self.__class__, environ=environ)                # 向接受通知的注册者发送通知try:request = self.request_class(environ)                                           # 调用WSGIRequest实例化请求except UnicodeDecodeError:logger.warning('Bad Request (UnicodeDecodeError)',exc_info=sys.exc_info(),extra={'status_code': 400,})response = http.HttpResponseBadRequest()else:response = self.get_response(request)                                           # 调用处理方法处理requestresponse._handler_class = self.__class__                                            # 设置_handler_class 为当前处理的类status = '%d %s' % (response.status_code, response.reason_phrase)                   # 相应结果的状态码和对应描述response_headers = [(str(k), str(v)) for k, v in response.items()]                  # 获取响应的响应头部信息for c in response.cookies.values():                                                 # 获取响应的cookie信息response_headers.append((str('Set-Cookie'), str(c.output(header=''))))          # 将cookie信息添加到头部信息中start_response(force_str(status), response_headers)                                 # 设置响应的响应头部信息if getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper'):      # 判断响应中是否有哦文件传输response = environ['wsgi.file_wrapper'](response.file_to_stream)return response                                                                     # 返回处理结果

首先调用BaseHandler中的load_middleware方法,Django中现在1.10系列版本中,所有的中间件对象都继承自MiddlewareMixin类:

class MiddlewareMixin(object):def __init__(self, get_response=None):                              # 将获取结果处理函数传入self.get_response = get_responsesuper(MiddlewareMixin, self).__init__()def __call__(self, request):response = Noneif hasattr(self, 'process_request'):                            # 检查是否有process_request属性有就调用response = self.process_request(request) if not response:                                                # response无结果则执行response = self.get_response(request)if hasattr(self, 'process_response'):response = self.process_response(request, response)         # 调用完成后处理return response

此时,我们继续查看BaseHandler的分析:

class BaseHandler(object):def __init__(self):self._request_middleware = None                                                 # 初始化请求中间件,view中间件等值self._view_middleware = Noneself._template_response_middleware = Noneself._response_middleware = Noneself._exception_middleware = Noneself._middleware_chain = Nonedef load_middleware(self):"""Populate middleware lists from settings.MIDDLEWARE (or the deprecatedMIDDLEWARE_CLASSES).Must be called after the environment is fixed (see __call__ in subclasses)."""self._request_middleware = []                                                    # 请求中间件self._view_middleware = []                                                       # view中间件self._template_response_middleware = []                                          # 模板中间件self._response_middleware = []                                                   # 响应中间件self._exception_middleware = []                                                  # 错误处理中间件if settings.MIDDLEWARE is None:                                                  # 如果配置文件中没有找到中间件配置warnings.warn("Old-style middleware using settings.MIDDLEWARE_CLASSES is ""deprecated. Update your middleware and use settings.MIDDLEWARE ""instead.", RemovedInDjango20Warning)                                                                            # 提示旧的中间件写法,需要更新到新的中间件写法handler = convert_exception_to_response(self._legacy_get_response)           # 处理中间件,由于该处理方法只是兼容旧方法,所以不在此分析for middleware_path in settings.MIDDLEWARE_CLASSES:mw_class = import_string(middleware_path)try:mw_instance = mw_class()except MiddlewareNotUsed as exc:if settings.DEBUG:if six.text_type(exc):logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc)else:logger.debug('MiddlewareNotUsed: %r', middleware_path)continueif hasattr(mw_instance, 'process_request'):self._request_middleware.append(mw_instance.process_request)if hasattr(mw_instance, 'process_view'):self._view_middleware.append(mw_instance.process_view)if hasattr(mw_instance, 'process_template_response'):self._template_response_middleware.insert(0, mw_instance.process_template_response)if hasattr(mw_instance, 'process_response'):self._response_middleware.insert(0, mw_instance.process_response)if hasattr(mw_instance, 'process_exception'):self._exception_middleware.insert(0, mw_instance.process_exception)else:                                                                                   # 新的中间件的写法handler = convert_exception_to_response(self._get_response)                         # 将处理响应包装出错处理for middleware_path in reversed(settings.MIDDLEWARE):                               # 获取中间件的包路径middleware = import_string(middleware_path)                                     # 导入中间件包类try:mw_instance = middleware(handler)                                           # 初始化该类except MiddlewareNotUsed as exc:if settings.DEBUG:if six.text_type(exc):logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc)else:logger.debug('MiddlewareNotUsed: %r', middleware_path)continueif mw_instance is None:raise ImproperlyConfigured('Middleware factory %s returned None.' % middleware_path)if hasattr(mw_instance, 'process_view'):                                        # 如果该中间件实例有该方法则添加到列表中self._view_middleware.insert(0, mw_instance.process_view)if hasattr(mw_instance, 'process_template_response'):self._template_response_middleware.append(mw_instance.process_template_response)if hasattr(mw_instance, 'process_exception'):self._exception_middleware.append(mw_instance.process_exception)handler = convert_exception_to_response(mw_instance)                            # 对该实例运行出错的情况进行处理# We only assign to this when initialization is complete as it is used# as a flag for initialization being complete.self._middleware_chain = handler                                                        # 将中间件最后一个进行出错处理的对象传入def make_view_atomic(self, view):                               non_atomic_requests = getattr(view, '_non_atomic_requests', set())                      # 获取处理view是否有不是使用事务处理属性for db in connections.all():if db.settings_dict['ATOMIC_REQUESTS'] and db.alias not in non_atomic_requests:     # 如果数据库配置每个请求一个事务并且该数据库不再不使用事务请求列表中view = transaction.atomic(using=db.alias)(view)                                 # 处理事务就是一个事务处理return viewdef get_exception_response(self, request, resolver, status_code, exception):return get_exception_response(request, resolver, status_code, exception, self.__class__)def get_response(self, request):"""Return an HttpResponse object for the given HttpRequest."""# Setup default url resolver for this threadset_urlconf(settings.ROOT_URLCONF)                                                      # 设置url配置response = self._middleware_chain(request)                                              # 调用107行的handler处理,该方法会调用_get_response# This block is only needed for legacy MIDDLEWARE_CLASSES; if# MIDDLEWARE is used, self._response_middleware will be empty.try:# Apply response middleware, regardless of the responsefor middleware_method in self._response_middleware:                                 # 检查response中间件方法response = middleware_method(request, response)                                 # 调用并处理# Complain if the response middleware returned None (a common error).if response is None:raise ValueError("%s.process_response didn't return an ""HttpResponse object. It returned None instead."% (middleware_method.__self__.__class__.__name__))except Exception:  # Any exception should be gathered and handledsignals.got_request_exception.send(sender=self.__class__, request=request)response = self.handle_uncaught_exception(request, get_resolver(get_urlconf()), sys.exc_info())response._closable_objects.append(request)# If the exception handler returns a TemplateResponse that has not# been rendered, force it to be rendered.if not getattr(response, 'is_rendered', True) and callable(getattr(response, 'render', None)):      # 获取是否是渲染的并且渲染方法能够调用response = response.render()                                                                    # 渲染结果if response.status_code == 404:logger.warning('Not Found: %s', request.path,extra={'status_code': 404, 'request': request},)return response                                                                                     # 返回处理结果def _get_response(self, request):"""Resolve and call the view, then apply view, exception, andtemplate_response middleware. This method is everything that happensinside the request/response middleware."""response = None                                                                 # 设置返回值为空if hasattr(request, 'urlconf'):                                                 # 检查request是否有urlconf属性urlconf = request.urlconf                                                   set_urlconf(urlconf)                                                        # 设置urlconf,由于是线程安全,所以会设置    resolver = get_resolver(urlconf)                                            # 获取url解析处理函数else:resolver = get_resolver()                                                   # 如果没有改属性就获取默认settings中的urlresolver_match = resolver.resolve(request.path_info)                            # 根据传入的请求路径解析匹配的处理函数callback, callback_args, callback_kwargs = resolver_match                       # 获取配置的处理函数request.resolver_match = resolver_match# Apply view middlewarefor middleware_method in self._view_middleware:response = middleware_method(request, callback, callback_args, callback_kwargs)       # 检查是否视图函数中的中间件处理函数if response:breakif response is None:      wrapped_callback = self.make_view_atomic(callback)                                    # 检查是否是一个请求就是一个事务try:response = wrapped_callback(request, *callback_args, **callback_kwargs)           # 调用处理回调函数处理请求except Exception as e:response = self.process_exception_by_middleware(e, request)# Complain if the view returned None (a common error).if response is None:if isinstance(callback, types.FunctionType):    # FBVview_name = callback.__name__else:                                           # CBVview_name = callback.__class__.__name__ + '.__call__'raise ValueError("The view %s.%s didn't return an HttpResponse object. It ""returned None instead." % (callback.__module__, view_name))# If the response supports deferred rendering, apply template# response middleware and then render the responseelif hasattr(response, 'render') and callable(response.render):                         # 检查响应是否有renderfor middleware_method in self._template_response_middleware:                        # 如果有调用模板中间函数处理response = middleware_method(request, response)                             # Complain if the template response middleware returned None (a common error).if response is None:raise ValueError("%s.process_template_response didn't return an ""HttpResponse object. It returned None instead."% (middleware_method.__self__.__class__.__name__))try:response = response.render()                                                    # 渲染返回结果except Exception as e:response = self.process_exception_by_middleware(e, request)                     # 调用中间件错误函数处理return response                                                                         # 返回响应def process_exception_by_middleware(self, exception, request):"""Pass the exception to the exception middleware. If no middlewarereturn a response for this exception, raise it."""for middleware_method in self._exception_middleware:response = middleware_method(request, exception)if response:return responseraisedef handle_uncaught_exception(self, request, resolver, exc_info):"""Allow subclasses to override uncaught exception handling."""return handle_uncaught_exception(request, resolver, exc_info)def _legacy_get_response(self, request):"""Apply process_request() middleware and call the main _get_response(),if needed. Used only for legacy MIDDLEWARE_CLASSES."""response = None# Apply request middlewarefor middleware_method in self._request_middleware:response = middleware_method(request)if response:breakif response is None:response = self._get_response(request)return response

当handler初始化时,先调用load_middleware初始化,配置文件中的中间件,然后将中间价的方法添加到列表中,然后将_get_resposne函数当做参宿传入中间件实例中,然后当调用handler的get_response方法时,调用经过中间包裹的_get_response方法,然后调用中间件中获取的方法,然后经过函数处理后就返回数据,此时一个完整的handler处理就完成。
此时我们继续url中配置的view视图函数根据官方文档给出的例子

# url配置
urlpatterns = [# ex: /polls/url(r'^$', views.index, name='index'),# ex: /polls/5/url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),]
# 视图处理函数
def detail(request, question_id):return HttpResponse("You're looking at question %s." % question_id)
此时的回调函数就是普通的处理函数,此时的detail会在该处调用
     response = wrapped_callback(request, *callback_args, **callback_kwargs)           # 调用处理回调函数处理请求
如果视图处理函数想是一般的视图处理函数,像restful这样的风格,根据http的方法调用不同的函数处理,此时的url与视图函数如下:
# url配置
urlpatterns = [url(r'^(?P<pk>[0-9]+)/$', views.DetailView.as_view(), name='detail'),]
# 视图处理函数
class DetailView(generic.DetailView):model = Questiontemplate_name = 'polls/detail.html'
使用这种方法就减少了重复代码,使代码更为简洁,其中DetailView继承自View,我们分析一下View源码:
class View(object):"""Intentionally simple parent class for all views. Only implementsdispatch-by-method and simple sanity checking."""http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']       # 支持的http请求方法def __init__(self, **kwargs):"""Constructor. Called in the URLconf; can contain helpful extrakeyword 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)@classonlymethoddef 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'):                                  # 如果有get方法,没有head方法,则将head方法定向为get方法处理self.head = self.getself.request = request                                                                  # 将request赋值给保存self.args = args                                                                        # 将输入参数设为属性self.kwargs = kwargs                                                                    # 将输入key参数设为属性return self.dispatch(request, *args, **kwargs)                                          # 调用处理函数view.view_class = cls                                                                       # 设置view函数的视图类属性view.view_initkwargs = initkwargs                                                           # 设置view函数参数属性# take name and docstring from classupdate_wrapper(view, cls, updated=())                                                       # 更新view的属性# and possible attributes set by decorators# like csrf_exempt from dispatchupdate_wrapper(view, cls.dispatch, assigned=())return view                                                                                 # 返回定义的view函数def dispatch(self, request, *args, **kwargs):                                                   # 根据http请求方法定向到相应方法# 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_allowedreturn handler(request, *args, **kwargs)                                                    # 调用handler处理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())                                 # 调用http中的方法不被允许处理函数处理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 responsedef _allowed_methods(self):return [m.upper() for m in self.http_method_names if hasattr(self, m)]                      # 返回当前类允许的方法

在url中,定义了DetailView.as_view(),此时就调用View类中的as_view方法,此时就会调用as_view中的view方法,该方法最终调用View类的self.dispatch,通过筛选后调用相应的handler处理。
对于视图函数的基本处理就这样,可以使用django自带的原生函数处理,也可以使用普通视图函数处理,普通视图函数比原生的函数封装的更好一点。

Django源码分析3:处理请求wsgi分析与视图View相关推荐

  1. Django源码分析4:staticfiles静态文件处理中间件分析

    django源码分析 本文环境python3.5.2,django1.10.x系列1.在上一篇文章中已经分析过handler的处理过程,其中load_middleware就是将配置的中间件进行初始化, ...

  2. Django源码分析2:本地运行runserver分析

    django源码分析 本文环境python3.5.2,django1.10.x系列1.根据上一篇文章分析了,django-admin startproject与startapp的分析流程后,根据dja ...

  3. Django源码分析9:model.py表结构的初始化概述

    django源码分析 本文环境python3.5.2,django1.10.x系列 django源码分析-model概述 Django项目中提供了内置的orm框架,只需要在models.py文件中添加 ...

  4. Django源码分析7:migrate命令的浅析

    django源码分析 本文环境python3.5.2,django1.10.x系列 django源码分析-migrate命令分析 Django项目中提供了,通过migrations操作数据库的结构的命 ...

  5. Django源码分析6:auth认证及登陆保持

    django源码分析 本文环境python3.5.2,django1.10.x系列 1.这次分析django框架中登陆认证与接口权限检查. 2.在后端开发中,难免会对接口进行权限验证,其中对于接口是否 ...

  6. Django源码分析5:session会话中间件分析

    django源码分析 本文环境python3.5.2,django1.10.x系列 1.这次分析django框架中的会话中间件. 2.会话保持是目前框架都支持的一个功能,因为http是无状态协议,无法 ...

  7. Django源码分析10:makemigrations命令概述

    django源码分析 本文环境python3.5.2,django1.10.x系列 django源码分析-makemigrations命令概述 Django项目中的数据库管理命令就是通过makemig ...

  8. Django源码分析8:单元测试test命令浅析

    django源码分析 本文环境python3.5.2,django1.10.x系列 django源码分析-test命令分析 Django项目中提供了,test命令行命令来执行django的单元测试,该 ...

  9. django源码阅读

    最近再看django-bootstrap-toolkit,一直困惑于静态文件的路径问题.所以只能从源码入手了.    从manage.py开始. manage.py 比较简单就几句话. #!/usr/ ...

最新文章

  1. 第十四周项目三-数组类模板
  2. java面试笔试大汇总(一)
  3. 【技术综述】深度学习中的数据增强方法都有哪些?
  4. Mysql中把varchar类型的字段转化为tinyint类型的字段
  5. 洛谷P3379 【模板】最近公共祖先(LCA)
  6. Flask+Gunicorn(协程)高并发的解决方法探究
  7. 18 个惊人的 GitHub 仓库
  8. 抖音算法暑期实习春招双双发车
  9. 【BZOJ 1491】 [NOI2007]社交网络
  10. 水系图一般在哪里找得到_虹吸雨水排水系统相较于传统重力排水有哪些优点?...
  11. Mathmatic绘制简易中国象棋棋盘
  12. win10家庭版解决“管理员已阻止你运行此应用”
  13. 三次样条插值matlab实现
  14. 哈工大软件过程与工具
  15. 2014年实习生招聘之百度上海研发中心实习生招聘电话面试—2014/04/18
  16. C++入坑系列(五)之函数指针
  17. OPC客户端分析 —— 读和写
  18. 数据结构与算法(十二)并查集(Union Find)及时间复杂度分析
  19. 百望云纳税申报管理解决方案,引领企业智慧税务建设
  20. java渡一教育百度云_小白学java第1篇(视频课程为渡一教育)

热门文章

  1. AI 不可以作为专利认证发明人,“因为它不是人”
  2. 对网络骚扰和霸凌说不!神经网络可以做得更好
  3. 用算法改造过的植物肉,有兴趣试试么?
  4. 这款耳机一点不输千元级的AirPods
  5. 阿里披露AI完整布局,飞天AI平台首次亮相
  6. MediaPipe:Google Research 开源的跨平台多媒体机器学习模型应用框架
  7. DeepMind提图像生成的递归神经网络DRAW,158行Python代码复现
  8. 新技术“红”不过十年,半监督学习为什么是个例外?
  9. 速度提升270倍!微软和浙大联合推出全新语音合成系统FastSpeech
  10. NIPS2018 | 腾讯AI Lab入选20篇论文,含2篇Spotlight