django源码分析——处理请求到wsgi及视图view

Posted thinheader

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了django源码分析——处理请求到wsgi及视图view相关的知识,希望对你有一定的参考价值。

本文环境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 useful
    for Django‘s internal server (runserver); external WSGI servers should just
    be configured to point to the correct application object directly.

    If settings.WSGI_APPLICATION is not set (is ``None``), we just return
    whatever ``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中的WSGIHandler
        return 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 WSGI
    callable.

    Allows us to avoid making django.core.handlers.WSGIHandler public API, in
    case 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 = WSGIRequest

    def __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)                                           # 调用处理方法处理request

        response._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_response
        super(MiddlewareMixin, self).__init__()

    def __call__(self, request):
        response = None
        if 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 = None
        self._template_response_middleware = None
        self._response_middleware = None
        self._exception_middleware = None
        self._middleware_chain = None

    def load_middleware(self):
        """
        Populate middleware lists from settings.MIDDLEWARE (or the deprecated
        MIDDLEWARE_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)
                    continue

                if 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)
                    continue

                if 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 view

    def 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 thread
        set_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 response
            for 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 handled
            signals.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, and
        template_response middleware. This method is everything that happens
        inside 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中的url

        resolver_match = resolver.resolve(request.path_info)                            # 根据传入的请求路径解析匹配的处理函数
        callback, callback_args, callback_kwargs = resolver_match                       # 获取配置的处理函数
        request.resolver_match = resolver_match

        # Apply view middleware
        for middleware_method in self._view_middleware:
            response = middleware_method(request, callback, callback_args, callback_kwargs)       # 检查是否视图函数中的中间件处理函数
            if response:
                break

        if 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):    # FBV
                view_name = callback.__name__
            else:                                           # CBV
                view_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 response
        elif hasattr(response, ‘render‘) and callable(response.render):                         # 检查响应是否有render
            for 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 middleware
        return a response for this exception, raise it.
        """
        for middleware_method in self._exception_middleware:
            response = middleware_method(request, exception)
            if response:
                return response
        raise

    def 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 middleware
        for middleware_method in self._request_middleware:
            response = middleware_method(request)
            if response:
                break

        if 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 = Question
    template_name = ‘polls/detail.html

 

使用这种方法就减少了重复代码,使代码更为简洁,其中DetailView继承自View,我们分析一下View源码:
  • 1
  • 2
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‘]       # 支持的http请求方法

    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‘):                                  # 如果有get方法,没有head方法,则将head方法定向为get方法处理
                self.head = self.get
            self.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 class
        update_wrapper(view, cls, updated=())                                                       # 更新view的属性

        # and possible attributes set by decorators
        # like csrf_exempt from dispatch
        update_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_allowed
        return 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 response

    def _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源码分析——处理请求到wsgi及视图view的主要内容,如果未能解决你的问题,请参考以下文章

django源码解读——从wsgi到django的httprequest请求对象生成

Django CBV源码分析

django中CBV源码分析

python-django的生命周期

django源码解析一(请求处理流程)

4 django篇