How Django works?


Django是如何工作的?

概念

以線上版本中Django和Nginx、uwsgi搭配,這里首先要了解uWSGI、uwsgi、WSGI分別代表着什么,其中uWSGI實現了uwsgi、WSGI、HTTP協議的Web服務器,WSGI是通信協議,而uwsgi則是線路協議。

流程

當用戶啟動Nginx以后,Nginx會直接處理靜態資源請求,動態資源請求則轉發給uWSGI服務器。

調用get_wsgi_application創建WSGIHandler對象

Web應用啟動以后,在settings.py中會調用該字段項WSGI_APPLICATION,這個字段項指向的是項目的wsgi.py文件,在這個文件中,調用了get_wsgi_application()創建了application,如下:

import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "Sparrow.settings")
application = get_wsgi_application()

接下來再看get_wsgi_application的具體實現:

import django
from django.core.handlers.wsgi import WSGIHandler
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)
    return WSGIHandler()

里面調用了setup函數完成了一下動作:

  1. 初始化app配置和加載app模塊;
  2. 加載model模塊;
  3. 運行app配置的開始函數;

同時返回了WSGIHandler對象,這個對象繼承自BaseHandler對象,在WSGIHandler初始化的時候還調用了BaseHandler對象的load_middleware()方法來加載中間件,也就是在settings.py中的MIDDLEWARE包含的所有中間件;注意,這里只有在初始化的時候調用,也就是只會加載一次即可。

加載中間件

BaseHandler中,分別用多個數組保存對應的中間件的回調函數,分別是:

  • _request_middleware
  • _view_middleware
  • _template_response_middleware
  • _response_middleware
  • _exception_middleware

在加載的時候,則會根據給定的類創建對應的中間件,判斷中間件是否有對應的函數,有則將該函數加入到對應的數組中,如下:

    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 = []  # 處理視圖
        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

構造WSGIRequest對象

WSGIHander初始化完成之后,然后它給調度程序發送一個信號request_started,並且將environ作為參數一塊傳遞過去;同時還構造WSGIRequest請求對象,因為其繼承的是HttpRequest,所以里面封裝了HTTP協議的內容,比如meta、cookie、content_type等等;如下:

request_class = WSGIRequest
request = self.request_class(environ)

處理response_middleware

接下來根據前面構造好的request作為參數通過get_response方法,在這個方法中會遍歷_response_middleware數組中的每個方法並且獲取到相應的響應信息,在這里對這些響應信息進行封裝,設置一些cookie、headers等等;如下:

    def __call__(self, environ, start_response):
        set_script_prefix(get_script_name(environ))
        signals.request_started.send(sender=self.__class__, environ=environ)  # 表明開始請求
        request = self.request_class(environ)
        response = self.get_response(request)  # 獲取響應
        response._handler_class = self.__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():
            response_headers.append((str('Set-Cookie'), str(c.output(header=''))))
        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

URLConf通過url.py文件找到請求的URL對應的視圖函數

如果view不是一個函數,那么說明下面還有多個url,這種就是使用了include的情況,那么則會實例化RegexURLResolver對象,遞歸的往下找;相反,如果是一個函數,那么則會實例化RegexURLPattern對象,當url匹配到這個正則表達式的時候,就會進入到相應的函數。如下:

def url(regex, view, kwargs=None, name=None):
    if isinstance(view, (list, tuple)):
        # For include(...) processing.
        urlconf_module, app_name, namespace = view
        return RegexURLResolver(regex, urlconf_module, kwargs, app_name=app_name, namespace=namespace)
    elif callable(view):
        return RegexURLPattern(regex, view, kwargs, name)
    else:
        raise TypeError('view must be a callable or a list/tuple in the case of include().')

View Middlewares被訪問,它同樣可以對request做一些處理或者直接返回response

一旦知道了視圖函數和相關的參數,處理器就會查看它的 _view_middleware 列表,並調用其中的方法。

調用View中的函數

在view中執行相關的邏輯,可以選擇性的通過Models訪問底層的數據。如果需要的話,Views可以創建一個額外的Context,Context被當做變量傳給Template,比如將Models中獲取的數據作為Context變量傳遞給模版。

模版渲染

模版根據可能傳遞過來的信息填充到指定的位置,開始進行渲染,渲染后的內容返回給View。

Response Middlewares處理Response

在Response生成之前,會執行Response Middlewares的邏輯。

返回Response給用戶

當一個模版完成渲染,或者產生了其它合適的輸出,View就會產生一 個 django.http.HttpResponse 實例,然后設置一些Headers、Cookies等等。

request_finished

一旦 middleware完成了最后環節,處理器將發送一個信號 request_finished,訂閱這個信號的事件會清空並釋放任何使用中的資源。

Exception

上面的情況並沒有考慮到錯誤的情況,如果出錯執行 exception middleware 相關邏輯,會有信號got_request_exception進行通知,如下:

        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())

參考文章:

Django的一次請求到響應的流程


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM