Django 结构及处理流程分析

conf
主要有两个做用:1) 处理全局配置, 好比数据库、加载的应用、 MiddleWare等 2) 处理urls配置, 就是url与view的映射关系。
contrib (贡献)
由Django的开发者贡献的功能模块,不过既然都已经随版本发布,就表示是官方的。
core
Django的核心处理库,包括url分析、处理请求、缓存等,其中处理请求是核心了,好比处理fastcgi就是由wsgi.py处理。
db
顾名思义,处理与数据库相关的,就是ORM。
dispatch (分派,派遣)
其实这不是Django原创,是pydispatch库,主要处理消费者-工做者模式。
forms && newforms && oldforms
处理html的表单,不用多介绍。
middleware
中间件,就是处理HTTP的request和response的,相似插件。好比默认的common中间件的一个功能:当一个页面没有找对对应的 pattern时,会自定加上‘/’从新处理。好比访问/blog时,而定义的pattern是‘^blog/$’,因此找不到对应的pattern,会自动再用/blog/查找,固然前提是 APPEND_SLASH=True。
template
Django的模板
templatetags
处理Application的tag的wrapper,就是将INSTALLED_APPS中全部的templatetags目录添加到 django.templatetags目录中,则当使用 {{load blog}}记载tag时,就可使用 import django.templatetags.blog 方式加载了。不过这有一个问题,若是其余Application目录中也有blog.py,这会加载第一个出现blog.py的tag。其实在 Django中,有许多须要处理重名的地方,好比template,须要格外当心,这个后续在介绍。
utils
公共库,不少公用的类都在放在这里。
views
最基本的view方法。

Django 术语

在应用 Django 的时候,咱们常常听到一些术语:html

Project
指一个完整的Web服务,通常由多个模块组成。
Application
能够理解为模块,好比用户管理、博客管理等,包含了数据的组成和数据的显示,Applicaiton都须要在 "project/settings.py" 中  INSTALLED_APPS 的定义。
Middleware
就是处理request和response的插件, Middleware都须要在 "project/settings.py" 中 MIDDLEWARE_CLASSES 的定义。
Loader
模板加载器, 其实就是为了读取 Template 文件的类,默认的有经过文件系统加载和在 "Application/templates" 目录中加载,Loader都须要在 "project/settings.py" 中  TEMPLATE_LOADERS 的定义。

处理流程

下面以 fastcgi 方式为例,Django的版本是 "1.1" 。node

其实和其余Web框架同样,HTTP处理的流程大体相同(但和Tomcat等Java容器不相同),python

好比request->response.程序员

  1. 加载配置

    Django的配置都在 "Project/settings.py" 中定义,能够是Django的配置,也能够是自定义的配置,而且都经过 django.conf.settings 访问,很是方便。正则表达式

  2. 启动

    最核心动做的是经过 django.core.management.commands.runfcgi 的 Command 来启动,它运行 django.core.servers.fastcgi 中的 runfastcgi , runfastcgi 使用了 flup 的 WSGIServer 来启动 fastcgi 。而 WSGIServer 中携带了 django.core.handlers.wsgi 的 WSGIHandler 类的一个实例,经过 WSGIHandler来处理由Web服务器(好比Apache,Lighttpd等)传过来的请求,此时才是真正进入Django的世界。数据库

  3. 处理 Request

    当有HTTP请求来时, WSGIHandler 就开始工做了,它从 BaseHandler 继承而来。 WSGIHandler 为每一个请求建立一个 WSGIRequest 实例,而 WSGIRequest 是从 http.HttpRequest 继承而来。接下来就开始建立 Response 了.django

  4. 建立Response

    BaseHandler 的 get_response 方法就是根据 request 建立 response , 而具体生成 response 的动做就是执行 urls.py 中对应的view函数了,这也是 Django能够处理“友好URL”的关键步骤,每一个这样的函数都要返回一个 Response 实例。此时通常的作法是经过 loader 加载 template 并生成页面内容,其中重要的就是经过 ORM 技术从数据库中取出数据,并渲染到 Template 中,从而生成具体的页面了数组

  5. 处理Response

    Django 返回 Response 给 flup , flup 就取出 Response 的内容返回给 Web 服务器,由后者返回给浏览器。浏览器

总之, Django 在 fastcgi 中主要作了两件事:处理 Request 和建立 Response , 而它们对应的核心就是“urls分析”、“模板技术”和“ORM技术”,这些留在之后分析。附图缓存

 
 

详细全流程

一个 Request 到达了!

首先发生的是一些和 Django 有关(前期准备)的其余事情,分别是:

  1. 若是是 Apache/mod_python 提供服务,request 由 mod_python 建立的 django.core.handlers.modpython.ModPythonHandler 实例传递给 Django。
  2. 若是是其余服务器,则必须兼容 WSGI,这样,服务器将建立一个 django.core.handlers.wsgi.WsgiHandler 实例。

这两个类都继承自 django.core.handlers.base.BaseHandler,它包含对任何类型的 request 来讲都须要的公共代码。

有一个处理器了

当上面其中一个处理器实例化后,紧接着发生了一系列的事情:

  1. 这个处理器导入你的 Django 配置文件。
  2. 这个处理器导入 Django 的自定义例外类。
  3. 这个处理器呼叫它本身的 load_middleware 方法,加载全部列在 MIDDLEWARE_CLASSES 中的 middleware 类而且内省它们。

最后一条有点复杂,咱们仔细瞧瞧。

一 个 middleware 类能够渗入处理过程的四个阶段:request,view,response 和 exception。要作到这一点,只须要定义指定的、恰当的方法:process_request,process_view, process_response 和 process_exception。middleware 能够定义其中任何一个或全部这些方法,这取决于你想要它提供什么样的功能。

当处理器内省 middleware 时,它查找上述名字的方法,并创建四个列表做为处理器的实例变量:

  1. _request_middleware 是一个保存 process_request 方法的列表(在每一种状况下,它们是真正的方法,能够直接呼叫),这些方法来自于任一个定义了它们的 middleware 类。
  2. _view_middleware 是一个保存 process_view 方法的列表,这些方法来自于任一个定义了它们的 middleware 类。
  3. _response_middleware 是一个保存 process_response 方法的列表,这些方法来自于任一个定义了它们的 middleware 类。
  4. _exception_middleware 是一个保存 process_exception 方法的列表,这些方法来自于任一个定义了它们的 middleware 类。

绿灯:如今开始

如今处理器已经准备好真正开始处理了,所以它给调度程序发送一个信号 request_started(Django 内部的调度程序容许各类不一样的组件声明它们正在干什么,并能够写一些代码监听特定的事件。关于这一点目前尚未官方的文档,但在 wiki上有一些注释。)。接下来它实例化一个 django.http.HttpRequest 的子类。根据不一样的处理器,多是 django.core.handlers.modpython.ModPythonRequest 的一个实例,也多是 django.core.handlers.wsgi.WSGIRequest 的一个实例。须要两个不一样的类是由于 mod_python 和 WSGI APIs 以不一样的格式传入 request 信息,这个信息须要解析为 Django 可以处理的一个单独的标准格式。

一旦一个 HttpRequest 或者相似的东西存在了,处理器就呼叫它本身的 get_response 方法,传入这个 HttpRequest 做为惟一的参数。这里就是几乎全部真正的活动发生的地方。

Middleware,第一回合

get_response 作的第一件事就是遍历处理器的 _request_middleware 实例变量并呼叫其中的每个方法,传入 HttpRequest 的实例做为参数。这些方法能够选择短路剩下的处理并当即让 get_response 返回,经过返回自身的一个值(若是它们这样作,返回值必须是 django.http.HttpResponse 的一个实例,后面会讨论到)。若是其中之一这样作了,咱们会当即回到主处理器代码,get_response 不会等着看其它 middleware 类想要作什么,它直接返回,而后处理器进入 response 阶段。

然而,更通常的状况是,这里应用的 middleware 方法简单地作一些处理并决定是否增长,删除或补充 request 的属性。

关于解析

假设没有一个做用于 request 的 middleware 直接返回 response,处理器下一步会尝试解析请求的 URL。它在配置文件中寻找一个叫作 ROOT_URLCONF 的配置,用这个配置加上根 URL /,做为参数来建立 django.core.urlresolvers.RegexURLResolver 的一个实例,而后呼叫它的 resolve 方法来解析请求的 URL 路径。

URL resolver 遵循一个至关简单的模式。对于在 URL 配置文件中根据 ROOT_URLCONF 的配置产生的每个在 urlpatterns 列表中的条目,它会检查请求的 URL 路径是否与这个条目的正则表达式相匹配,若是是的话,有两种选择:

  1. 若是这个条目有一个能够呼叫的 include,resolver 截取匹配的 URL,转到 include 指定的 URL 配置文件并开始遍历其中 urlpatterns 列表中的每个条目。根据你 URL 的深度和模块性,这可能重复好几回。
  2. 不然,resolver 返回三个条目:匹配的条目指定的 view function;一个从 URL 获得的未命名匹配组(被用来做为 view 的位置参数);一个关键字参数字典,它由从 URL 获得的任意命名匹配组和从 URLConf 中获得的任意其它关键字参数组合而成。

注意这一过程会在匹配到第一个指定了 view 的条目时中止,所以最好让你的 URL 配置从复杂的正则过渡到简单的正则,这样能确保 resolver 不会首先匹配到简单的那一个而返回错误的 view function。

若是没有找到匹配的条目,resolver 会产生 django.core.urlresolvers.Resolver404 例外,它是 django.http.Http404 例外的子类。后面咱们会知道它是如何处理的。

Middleware,第二回合

一旦知道了所需的 view function 和相关的参数,处理器就会查看它的 _view_middleware 列表,并呼叫其中的方法,传入 HttpRequst,view function,针对这个 view 的位置参数列表和关键字参数字典。

还有,middleware 有可能介入这一阶段并强迫处理器当即返回。

进入 View

若是处理过程这时候还在继续的话,处理器会呼叫 view function。Django 中的 Views 不很严格由于它只须要知足几个条件:

  • 必须能够被呼叫。
  • 必须接受 django.http.HttpRequest 的实例做为第一位值参数。
  • 必须能产生一个例外或返回 django.http.HttpResponse 的一个实例。

除了这些,你就能够天马行空了。尽管如此,通常来讲,views 会使用 Django 的 database API 来建立,检索,更新和删除数据库的某些东西,还会加载并渲染一个模板来呈现一些东西给最终用户。

模板

Django 的模板系统有两个部分:一部分是给设计师使用的混入少许其它东西的 HTML,另外一部分是给程序员使用纯 Python。

从一个 HTML 做者的角度,Django 的模板系统很是简单,须要知道的仅有三个结构:

  1. 变量引用。在模板中是这样: {{ foo }}。
  2. 模板过滤。在上面的例子中使用过滤竖线是这样:{{ foo|bar }}。一般这用来格式化输出(好比:运行 Textile,格式化日期等等)。
  3. 模板标签。是这样:{% baz %}。这是模板的“逻辑”实现的地方,你能够 {% if foo %},{% for bar in foo %},等等,if 和 for 都是模板标签。

变量引用以一种很是简单的方式工做。若是你只是要打印变量,只要 {{ foo }},模板系统就会输出它。这里惟一的复杂状况是 {{ foo.bar }},这时模板系统按顺序尝试几件事:

  1. 首先它尝试一个字典方式的查找,看看 foo['bar'] 是否存在。若是存在,则它的值被输出,这个过程也随之结束。
  2. 若是字典查找失败,模板系统尝试属性查找,看看 foo.bar 是否存在。同时它还检查这个属性是否能够被呼叫,若是能够,呼叫之。
  3. 若是属性查找失败,模板系统尝试把它做为列表索引进行查找。

若是全部这些都失败了,模板系统输出配置 TEMPLATE_STRING_IF_INVALID 的值,默认是空字符串。

模板过滤就是简单的 Python functions,它接受一个值和一个参数,返回一个新的值。好比,date 过滤用一个 Python datetime 对象做为它的值,一个标准的 strftime 格式化字符串做为它的参数,返回对 datetime 对象应用了格式化字符串以后的结果。

模板标签用在事情有一点点复杂的地方,它是你了解 Django 的模板系统是如何真正工做的地方。

Django 模板的结构

在内部,一个 Django 模板体现为一个 “nodes” 集合,它们都是从基本的 django.template.Node 类继承而来。Nodes 能够作各类处理,但有一个共同点:每个 Node 必须有一个叫作 render 的方法,它接受的第二个参数(第一个参数,显然是 Node 实例)是 django.template.Context 的一个实例,这是一个相似于字典的对象,包含全部模板能够得到的变量。Node 的 render 方法必须返回一个字符串,但若是 Node 的工做不是输出(好比,它是要经过增长,删除或修改传入的 Context 实例变量中的变量来修改模板上下文),能够返回空字符串。

Django 包含许多 Node 的子类来提供有用的功能。好比,每一个内置的模板标签都被一个 Node 的子类处理(好比,IfNode 实现了 if 标签,ForNode 实现了 for 标签,等等)。全部内置标签能够在 django.template.defaulttags 找到。实际上,上面介绍的全部模板结构都是某种形式的 Nodes,纯文本也不例外。变量查找由 VariableNode 处理,出于天然,过滤也应用在 VariableNode 上,标签是各类类型的 Nodes,纯文本是一个 TextNode。

通常来讲,一个 view 渲染一个模板要通过下面的步骤,依次是:

  1. 加载须要渲染的模板。这是由 django.template.loader.get_template 完成的,它能利用这许多方法中的任意一个来定位须要的模板文件。 get_template 函数返回一个 django.template.Template 实例,其中包含通过解析的模板和用到的方法。
  2. 实例化一个 Context 用来渲染模板。若是用的是 Context 的子类 django.template.RequestContext,那么附带的上下文处理函数就会自动添加在 view 中没有定义的变量。Context 的构建器方法用一个键/值对的字典(对于模板,它将变为名/值变量)做为它惟一的参数,RequestContext 则用 HttpRequest 的一个实例和一个字典。
  3. 呼叫 Template 实例的 render 方法,Context 对象做为第一个位置参数。

Template 的 render 方法的返回值是一个字符串,它由 Template 中全部 Nodes 的 render 方法返回的值链接而成,呼叫顺序为它们出如今 Template 中的顺序。

关于 Response,一点点

一旦一个模板完成渲染,或者产生了其它某些合适的输出,view 就会负责产生一个 django.http.HttpResponse 实例,它的构建器接受两个可选的参数:

  1. 一个做为 response 主体的字符串(它应该是第一位置参数,或者是关键字参数 content)。大部分时间,这将做为渲染一个模板的输出,但不是必须这样,在这里你能够传入任何有效的 Python 字符串。
  2. 做 为 response 的 Content-Type header 的值(它应该是第二位置参数,或者是关键字参数 mine_type)。若是没有提供这个参数,Django 将会使用配置中 DEFAULT_MIME_TYPE 的值和 DEFAULT_CHARSET 的值,若是你没有在 Django 的全局配置文件中更改它们的话,分别是 “text/html” 和 “utf-8”。

Middleware,第三回合:例外

若是 view 函数,或者其中的什么东西,发生了例外,那么 get_response(我知道咱们已经花了些时间深刻 views 和 templates,可是一旦 view 返回或产生例外,咱们仍将重拾处理器中间的 get_response 方法)将遍历它的 _exception_middleware 实例变量并呼叫那里的每一个方法,传入 HttpResponse 和这个 exception 做为参数。若是顺利,这些方法中的一个会实例化一个 HttpResponse 并返回它。

仍然没有响应?

这时候有可能仍是没有获得一个 HttpResponse,这可能有几个缘由:

  1. view 可能没有返回值。
  2. view 可能产生了例外但没有一个 middleware 能处理它。
  3. 一个 middleware 方法试图处理一个例外时本身又产生了一个新的例外。

这时候,get_response 会回到本身的例外处理机制中,它们有几个层次:

  1. 若是 exception 是 Http404 而且 DEBUG 设置为 True,get_response 将执行 view django.views.debug.technical_404_response,传入 HttpRequest 和 exception 做为参数。这个 view 会展现 URL resolver 试图匹配的模式信息。
  2. 若是 DEBUG 是 False 而且例外是 Http404,get_response 会呼叫 URL resolver 的 resolve_404 方法。这个方法查看 URL 配置以判断哪个 view 被指定用来处理 404 错误。默认是 django.views.defaults.page_not_found,但能够在 URL 配置中给 handler404 变量赋值来更改。
  3. 对于任何其它类型的例外,若是 DEBUG 设置为 True,get_response 将执行 view django.views.debug.technical_500_response,传入 HttpRequest 和 exception 做为参数。这个 view 提供了关于例外的详细信息,包括 traceback,每个层次 stack 中的本地变量,HttpRequest 对象的详细描述和全部无效配置的列表。
  4. 若是 DEBUG 是 False,get_response 会呼叫 URL resolver 的 resolve_500 方法,它和 resolve_404 方法很是类似,这时默认的 view 是 django.views.defaults.server_error,但能够在 URL 配置中给 handler500 变量赋值来更改。

此外,对于除了 django.http.Http404 或 Python 内置的 SystemExit 以外的任何例外,处理器会给调度者发送信号 got_request_exception,在返回以前,构建一个关于例外的描述,把它发送给列在 Django 配置文件的 ADMINS 配置中的每个人。

Middleware,最后回合

如今,不管 get_response 在哪个层次上发生错误,它都会返回一个 HttpResponse 实例,所以咱们回处处理器的主要部分。一旦它得到一个 HttpResponse 它作的第一件事就是遍历它的 _response_middleware 实例变量并应用那里的方法,传入 HttpRequest 和 HttpResponse 做为参数。

注意对于任何想改变点什么的 middleware 来讲,这是它们的最后机会。

The check is in the mail

是该结束的时候了。一旦 middleware 完成了最后环节,处理器将给调度者发送信号 request_finished,对与想在当前的 request 中执行的任何东西来讲,这绝对是最后的呼叫。监听这个信号的处理者会清空并释听任何使用中的资源。好比,Django 的 request_finished 监听者会关闭全部数据库链接。

这件事发生之后,处理器会构建一个合适的返回值送返给实例化它的任何东西(如今,是一个恰当的 mod_python response 或者一个 WSGI 兼容的 response,这取决于处理器)并返回。

呼呼

结束了,从开始到结束,这就是 Django 如何处理一个 request。

相关文章
相关标签/搜索