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的一次请求到响应的流程

How Django works?的更多相关文章

  1. 【Django必备01】——什么是Django框架?有什么优势?模块组成介绍。

    01.什么是Django框架? Django是一个开放源代码的Web应用框架,由Python写成.采用了MTV的框架模式.使用这种架构,程序员可以方便.快捷地创建高品质.易维护.数据库驱动的应用程序. ...

  2. spring注解源码分析--how does autowired works?

    1. 背景 注解可以减少代码的开发量,spring提供了丰富的注解功能.我们可能会被问到,spring的注解到底是什么触发的呢?今天以spring最常使用的一个注解autowired来跟踪代码,进行d ...

  3. [译]如何使用 Docker 组件开发 Django 项目?

    原文地址:Django Development With Docker Compose and Machine 以下为译文 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包 ...

  4. 一 django框架?

    Django-1   一 什么是web框架? 框架,即framework,特指为解决一个开放性问题而设计的具有一定约束性的支撑结构,使用框架可以帮你快速开发特定的系统,简单地说,就是你用别人搭建好的舞 ...

  5. 干货福利:如何使用Python中Django模板?

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: 穆胜亮 篇文章将学习如何使用Django模板.模板是在Django ...

  6. CentOS下宝塔如何部署Django项目?

    基础环境 装好宝塔服务 宝塔里装好[Python项目管理器] 宝塔里装好[Nginx] 把Django项目代码发到服务器 把代码放到服务器上有两种方法: 方法一:服务器上安装Git,通过Git Clo ...

  7. How Hystrix Works?--官方

    https://github.com/Netflix/Hystrix/wiki/How-it-Works Contents Flow Chart Circuit Breaker Isolation T ...

  8. 54.1 怎样才算学会django? 知道这28个知识点才算会django2

    学到什么程度才算会django了?这篇文章帮你梳理一下 关于django2的28个不可不知的知识点总结: 1.cookie操作: -客户端本地存储的键值对 2.session操作: -服务器端可以保存 ...

  9. 怎样从外网访问内网Django?

    本地安装了一个Django,只能在局域网内访问,怎样从外网也能访问到本地的Django呢?本文将介绍具体的实现步骤. 准备工作 安装并启动Django 默认安装的Django端口是8000. 实现步骤 ...

随机推荐

  1. 2.配置Spring+SpringMvc+Mybatis(分库or读写分离)--Intellij IDAE 2016.3.5

    建立好maven多模块项目后,开始使用ssm传统的框架: 1.打开总工程下的pom.xml文件:添加如下代码: <!--全局的所有版本号定义--> <properties> & ...

  2. 解决安卓手机input获取焦点时会将底部固定定位按钮顶起的问题

    一个页面上有个固定在底部的按钮,页面中有个input框,点击input框获取焦点时,在苹果手机上没事,但在安卓手机上弹出的键盘会将按钮顶起来,这就很不好看了,想了个办法解决一下.之前一直觉得用inpu ...

  3. 15套java互联网架构师、高并发、集群、负载均衡、高可用、数据库设计、缓存、性能优化、大型分布式 项目实战视频教程

    * { font-family: "Microsoft YaHei" !important } h1 { color: #FF0 } 15套java架构师.集群.高可用.高可扩 展 ...

  4. 数据的ID名生成新的引用索引树

    <?php $arr= [ '0'=>[ "id"=>2, "name"=>"建材", "pid" ...

  5. 合并静态库出现 can't move temporary file错误

    静态库的制作就不说了很简单,网上也很多例子,这里主要讲下我合并通用静态库时候遇见的坑,在合并前注意.a文件一定要正确,我有一次scheme选了release但是device忘了换,结果怼着两个模拟器静 ...

  6. 【LeetCode】327. Count of Range Sum

    题目: Given an integer array nums, return the number of range sums that lie in [lower, upper] inclusiv ...

  7. HTML5 Web SQL 数据库操作

    Web SQL 数据库 API 并不是 HTML5 规范的一部分,但是它是一个独立的规范,引入了一组使用 SQL 操作客户端数据库的 APIs. 以下是规范中定义的三个核心方法: openDataba ...

  8. java程序员常见面试题目

      答:每当程序出现异常之后,如果程序没有进行相应的处理,则程序会出现中断现象.实际上,产生了异常之后,JVM会抛出一个异常类的实例化对象,如果此时使用了try语句捕获的话,则可以进行异常的处理,否则 ...

  9. 网络配置之nmcli

    使用nmcli命令配置网络 NetworkManager是管理和监控网络设置的守护进程,设备既就是网络接口,连接是对网络接口的配置,一个网络接口可以有多个连接配置,但同时只有一个连接配置生效. 1 配 ...

  10. c++数组易错点总结

    c++数组 1.只有在定义数组是才能使用初始化,此后就不能使用了,也不能将一个数组赋给另一个数组 int cards[4] = { 3 , 6 , 8 , 10}; //ok int hands[4] ...