0%

flask学习笔记(二)

从最简单的flask程序来了解学习Werkzeug与Jinjia2[使用方法+源码剖析]

在学习flask的过程中会遇到Werkzeug与Jinjia2两个模块,这两个模块在底层为flask提供了丰富的支持。
Werkzeug
官网上是这么介绍Werkzeug的:
Werkzeug在德语中是工具的意思,是一个综合的WSGI web应用库。它起初只是WSGI应用程序的各种实用程序的简单集合,现在已经成为最先进的WSGI实用程序库之一。

WSGI
所谓的WSGI为web服务器网关接口,在处理一个WSGI请求时,服务器会为应用程序提供环境信息及一个回调函数(Callback Function)。当应用程序完成处理请求后,透过前述的回调函数,将结果回传给服务器。WSGI定义了服务器和应用程序通信的标准
一个简单的符合WSGI的应用程序:

1
2
3
def application_callable(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
yield "Hello world!\n"

Web程序必须有一个 可调用对象
当收到一个请求后,服务器会通过 application_callable(environ, start_response) 调用应用。

  1. 其中environ是一个字典,包含请求的相关信息,如请求方式、请求路径等等。
  2. 而start_response为上述 可调用对象 中调用的函数,接受的参数为状态(status)和响应头(response_headers)。

从flask到Werkzeug
flask hellworld

1
2
3
4
5
6
7
8
9
10
from flask import Flask

app = Flask(__name__)

@app.route('/index')
def index():
return 'hello world'

if __name__ == '__main__':
app.run()

以下分析步骤仅考虑在一般情况下,只列出关键代码:

  1. 跟进app.run方法,在run方法中调用了werkzeug.serving的run_simple函数
  2. 跟进run_simple函数调用了inner函数
  3. inner函数中又调用了make_server
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    def inner():
    try:
    fd = int(os.environ["WERKZEUG_SERVER_FD"])
    except (LookupError, ValueError):
    fd = None
    srv = make_server(
    hostname,
    port,
    application,
    threaded,
    processes,
    request_handler,
    passthrough_errors,
    ssl_context,
    fd=fd,
    )
    if fd is None:
    log_startup(srv.socket)
    srv.serve_forever()
  4. make_server中调用了BaseWSGIServer
    1
    2
    3
    return BaseWSGIServer(
    host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd
    )
  5. BaseWSGIServer又继承了HTTPServer,HTTPServer继承了TCPServer,在TCPServer中我们看到了非常熟悉的东西:
    1
    2
    3
    self.socket = socket.socket(self.address_family, self.socket_type)
    self.socket.bind(self.server_address)
    self.socket.listen(self.request_queue_size)
    在我们初学python socket的时候,编写服务端的时候便是这样一个步骤,即创建套接字,绑定端口,持续监听;通过分析源码,我们浅显的搞清楚了从python应用到web服务后边函数调用链,也就是wsgi的原理,在应用与服务器之间架设桥梁。
    试想一下如果没有Werkzeug,flask不可能用短短几行代码跑起一个WEB应用,而正是有了Werkzeug在底层的支持,我们才能如此快捷的使用flask框架。

Jinjia2
Jinjia2是一种Python模板语言,具有很多友好的特性。Jinjia是日文中神庙的意思,对应英文为temple,和模板template非常相似。
与Werkzeug一样,从一个最简单的用到jinjia2模板渲染的程序来探究下jinjia2.

1
2
3
4
5
6
7
8
9
10
11
from flask import Flask, render_template

app = Flask(__name__, template_folder='./')

@app.route('/index')
def index():
name = 'lockcy'
return render_template('index.html', name=name)

if __name__ == '__main__':
app.run()

index.html

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>hello,{{name}}</h2>
</body>
</html>
  1. 跟进render_template,调用了_render
    1
    2
    3
    4
    5
    return _render(
    ctx.app.jinja_env.get_or_select_template(template_name_or_list),
    context,
    ctx.app,
    )
  2. 跟进_render函数。过程如下:发送信号,渲染模板,发送信号,返回渲染后字符。
    1
    2
    3
    4
    5
    6
    7
    def _render(template, context, app):
    """Renders the template and fires the signal"""

    before_render_template.send(app, template=template, context=context) # signal.py before_render_template = _signals.signal("before-render-template")
    rv = template.render(context) # rv is the rendered results
    template_rendered.send(app, template=template, context=context) # signal.py before_render_template = _signals.signal("template_rendered")
    return rv
  3. 关于flask中的信号量与模板源码的细节以后再讨论,这里只是简单过下Jinjia2的模板渲染流程。
  4. 再看下Jinjia2的一些基本语法:
    1
    2
    3
    4
    5
    6
    7
    控制结构 *{% %}
    变量取值 {{ }}
    注释 {# #}
    例:
    {% for name in names %}
    <h2>hello,{{name}}</h2>
    {% endfor %}