在应用 Django 的时候,我们经常听到一些术语:
下面以 fastcgi 方式为例,Django的版本是 "1.1" 。
其实和其他Web框架一样,HTTP处理的流程大致相同(但和Tomcat等Java容器不相同),
比如request->response.
Django的配置都在 "Project/settings.py" 中定义,可以是Django的配置,也可以是自定义的配置,并且都通过 django.conf.settings 访问,非常方便。
最核心动作的是通过 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的世界。
当有HTTP请求来时, WSGIHandler 就开始工作了,它从 BaseHandler 继承而来。 WSGIHandler 为每个请求创建一个 WSGIRequest 实例,而 WSGIRequest 是从 http.HttpRequest 继承而来。接下来就开始创建 Response 了.
BaseHandler 的 get_response 方法就是根据 request 创建 response , 而具体生成 response 的动作就是执行 urls.py 中对应的view函数了,这也是 Django可以处理“友好URL”的关键步骤,每个这样的函数都要返回一个 Response 实例。此时一般的做法是通过 loader 加载 template 并生成页面内容,其中重要的就是通过 ORM 技术从数据库中取出数据,并渲染到 Template 中,从而生成具体的页面了
Django 返回 Response 给 flup , flup 就取出 Response 的内容返回给 Web 服务器,由后者返回给浏览器。
总之, Django 在 fastcgi 中主要做了两件事:处理 Request 和创建 Response , 而它们对应的核心就是“urls分析”、“模板技术”和“ORM技术”,这些留在以后分析。附图
首先发生的是一些和 Django 有关(前期准备)的其他事情,分别是:
这两个类都继承自 django.core.handlers.base.BaseHandler,它包含对任何类型的 request 来说都需要的公共代码。
当上面其中一个处理器实例化后,紧接着发生了一系列的事情:
最后一条有点复杂,我们仔细瞧瞧。
一 个 middleware 类可以渗入处理过程的四个阶段:request,view,response 和 exception。要做到这一点,只需要定义指定的、恰当的方法:process_request,process_view, process_response 和 process_exception。middleware 可以定义其中任何一个或所有这些方法,这取决于你想要它提供什么样的功能。
当处理器内省 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 作为唯一的参数。这里就是几乎所有真正的活动发生的地方。
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 路径是否与这个条目的正则表达式相匹配,如果是的话,有两种选择:
注意这一过程会在匹配到第一个指定了 view 的条目时停止,因此最好让你的 URL 配置从复杂的正则过渡到简单的正则,这样能确保 resolver 不会首先匹配到简单的那一个而返回错误的 view function。
如果没有找到匹配的条目,resolver 会产生 django.core.urlresolvers.Resolver404 例外,它是 django.http.Http404 例外的子类。后面我们会知道它是如何处理的。
一旦知道了所需的 view function 和相关的参数,处理器就会查看它的 _view_middleware 列表,并呼叫其中的方法,传入 HttpRequst,view function,针对这个 view 的位置参数列表和关键字参数字典。
还有,middleware 有可能介入这一阶段并强迫处理器立即返回。
如果处理过程这时候还在继续的话,处理器会呼叫 view function。Django 中的 Views 不很严格因为它只需要满足几个条件:
除了这些,你就可以天马行空了。尽管如此,一般来说,views 会使用 Django 的 database API 来创建,检索,更新和删除数据库的某些东西,还会加载并渲染一个模板来呈现一些东西给最终用户。
Django 的模板系统有两个部分:一部分是给设计师使用的混入少量其它东西的 HTML,另一部分是给程序员使用纯 Python。
从一个 HTML 作者的角度,Django 的模板系统非常简单,需要知道的仅有三个结构:
变量引用以一种非常简单的方式工作。如果你只是要打印变量,只要 {{ foo }},模板系统就会输出它。这里唯一的复杂情况是 {{ foo.bar }},这时模板系统按顺序尝试几件事:
如果所有这些都失败了,模板系统输出配置 TEMPLATE_STRING_IF_INVALID 的值,默认是空字符串。
模板过滤就是简单的 Python functions,它接受一个值和一个参数,返回一个新的值。比如,date 过滤用一个 Python datetime 对象作为它的值,一个标准的 strftime 格式化字符串作为它的参数,返回对 datetime 对象应用了格式化字符串之后的结果。
模板标签用在事情有一点点复杂的地方,它是你了解 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 渲染一个模板要经过下面的步骤,依次是:
Template 的 render 方法的返回值是一个字符串,它由 Template 中所有 Nodes 的 render 方法返回的值连接而成,呼叫顺序为它们出现在 Template 中的顺序。
一旦一个模板完成渲染,或者产生了其它某些合适的输出,view 就会负责产生一个 django.http.HttpResponse 实例,它的构建器接受两个可选的参数:
如果 view 函数,或者其中的什么东西,发生了例外,那么 get_response(我知道我们已经花了些时间深入 views 和 templates,但是一旦 view 返回或产生例外,我们仍将重拾处理器中间的 get_response 方法)将遍历它的 _exception_middleware 实例变量并呼叫那里的每个方法,传入 HttpResponse 和这个 exception 作为参数。如果顺利,这些方法中的一个会实例化一个 HttpResponse 并返回它。
这时候有可能还是没有得到一个 HttpResponse,这可能有几个原因:
这时候,get_response 会回到自己的例外处理机制中,它们有几个层次:
此外,对于除了 django.http.Http404 或 Python 内置的 SystemExit 之外的任何例外,处理器会给调度者发送信号 got_request_exception,在返回之前,构建一个关于例外的描述,把它发送给列在 Django 配置文件的 ADMINS 配置中的每一个人。
现在,无论 get_response 在哪一个层次上发生错误,它都会返回一个 HttpResponse 实例,因此我们回到处理器的主要部分。一旦它获得一个 HttpResponse 它做的第一件事就是遍历它的 _response_middleware 实例变量并应用那里的方法,传入 HttpRequest 和 HttpResponse 作为参数。
注意对于任何想改变点什么的 middleware 来说,这是它们的最后机会。
是该结束的时候了。一旦 middleware 完成了最后环节,处理器将给调度者发送信号 request_finished,对与想在当前的 request 中执行的任何东西来说,这绝对是最后的呼叫。监听这个信号的处理者会清空并释放任何使用中的资源。比如,Django 的 request_finished 监听者会关闭所有数据库连接。
这件事发生以后,处理器会构建一个合适的返回值送返给实例化它的任何东西(现在,是一个恰当的 mod_python response 或者一个 WSGI 兼容的 response,这取决于处理器)并返回。
结束了,从开始到结束,这就是 Django 如何处理一个 request。