1. 初期化
この章では、Flask アプリケーションのさまざまな部分を学習します。同時に、最初の Flask Web アプリケーションを作成して実行します。
すべての Flask アプリケーションはアプリケーション インスタンスを作成する必要があります。クライアントから受信したすべてのリクエストは、Web サーバー ゲートウェイ インターフェイス プロトコルを使用して処理するためにこのオブジェクトに渡されます。このアプリケーション インスタンスは Flask クラスのオブジェクトであり、通常は次のメソッドを使用して作成されます:
from flask import Flask app = Flask(__name__)
Flask クラス コンストラクターに必要な唯一のパラメーターは、アプリケーションのメイン モジュールまたはパッケージです。ほとんどのアプリケーションでは、Python の __name__ 変数が渡す必要がある正しい値です。
注: Flask 開発者にとって、Flask アプリケーション コンストラクターに渡される name パラメーターは混乱を招く可能性があります。 Flask はこのパラメータを使用してアプリケーションのルート ディレクトリを決定し、今後このパスに相対してリソース ファイルを見つけられるようにします。
より複雑なアプリケーション インスタンスの初期化については後ほど説明しますが、単純なアプリケーションの場合はこれで十分です。
2. ルーティングおよびビュー機能
Web ブラウザーなどのクライアントは Web サービスにリクエストを送信し、Flask アプリケーション インスタンスに送信します。アプリケーション インスタンスは、各 URL リクエストに対してどのコードを実行する必要があるかを認識する必要があるため、Python 関数への URL のマップを設定します。 URL と関数間の接続を確立するこれらの操作は、ルーティングと呼ばれます。
Flask アプリケーションでルートを定義する最も便利な方法は、アプリケーション インスタンスで app.route デコレーターを明示的に定義することによって、装飾された関数をルートとして登録することです。次の例は、デコレータを使用してルートを宣言する方法を示しています。
@app.route('/') def index(): return '<h1>Hello World!</h1>'
注: デコレータは Python 言語の標準機能であり、さまざまな方法で関数の動作を変更できます。一般的なパターンは、デコレーターを使用して関数をイベント ハンドラーとして登録することです。
前の例では、index() 関数をアプリケーションのルート URL のイベント ハンドラーとして登録します。このアプリケーションがサーバー上にデプロイされ、www.example.com ドメイン名にバインドされている場合、ブラウザのアドレス バーに http://www.php.cn/ と入力すると、index() がトリガーされてサービスが実行されます。クライアントが受け取るこの関数の戻り値をレスポンスと呼びます。クライアントが Web ブラウザの場合、応答はユーザーに表示されるドキュメントです。
index()に似た関数はビュー関数と呼ばれます。ビューによって返される応答は、HTML コンテンツの単純な文字列である場合もありますが、後でわかるように、より複雑な形式をとることもあります。
注: 応答文字列は Python コードに埋め込まれているため、コードの制御が難しくなります。ここでは応答の概念のみを紹介します。第 3 章では、応答を生成する正しい方法を学びます。
毎日使用する Web サイトの URL の一部がどのように形成されているかに注目すると、多くの URL に変数があることがわかります。たとえば、Facebook プロフィール ページの URL は http://www.php.cn/;username> なので、ユーザー名はその一部です。 Flask は、ルート デコレータの特別な構文を使用して、これらのタイプの URL をサポートします。次の例では、動的名前コンポーネントを持つルートを定義します。
@app.route('/user/<name>') def user(name): return '<h1>Hello, %s!</h1>' % name
山かっこで囲まれた部分は動的部分であるため、静的部分に一致する URL はすべてこのルートにマッピングされます。 view 関数が呼び出されると、Flask は動的コンポーネントをパラメータとして送信します。前の例の view 関数では、このパラメーターは応答としてパーソナライズされた挨拶を生成するために使用されます。
ルーティングの動的コンポーネントはデフォルトで文字列ですが、他のタイプとして定義することもできます。たとえば、ルート /user/
3. サービスの起動
アプリケーション インスタンスには、Flask 統合 Web サービスを起動するための run メソッドがあります:
if __name__ == '__main__': app.run(debug=True)
__name__ == '__main__' は、Web サービスがすでに起動されていることを確認するためにここで使用されます。スクリプトが即時に実行されると開始されます。スクリプトが別のスクリプトによってインポートされると、そのスクリプトは親スクリプトとして扱われ、別のサービスが開始されるため、app.run() 呼び出しはスキップされます。
サービスが開始されると、リクエストを待機するループに入り、リクエストを処理します。このループは、Ctrl+C を押すなどしてアプリケーションが停止されるまで継続します。
Web サービスの動作モードを app.run() に設定するために使用できるオプション パラメーターがいくつかあります。開発中に、デバッガとリローダをアクティブにするデバッグ モードを簡単にオンにすることができます。これは、debug を True として渡すことによって行われます。
注: Flask が提供する Web サービスは実稼働環境では使用されません。第 17 章では、実稼働環境での Web サービスについて学習します。
4. 完全なアプリケーション
前のセクションでは、Flask Web アプリケーションのさまざまな部分について学びました。今度は、Flask Web アプリケーションを作成します。 hello.py アプリケーション スクリプト全体は、前述の 3 つの部分を 1 つのファイルに結合するだけです。応用例2−1を示す。
hello.py の例: 完全な Flask アプリケーション
from flask import Flask app = Flask(__name__) @app.route('/') def index(): return 'Hello World!
' if __name__ == '__main__': app.run(debug=True)
建议:如果你有克隆在GitHub上的应用程序,你现在可以运行git checkout 2a来切换到这个版本的应用程序。
运行应用程序之前,请确保你在之前创建的虚拟环境已经是激活状态且已安装Flask。现在打开你的web浏览器并在地址栏输入 http://www.php.cn/:5000/ 。下图显示连接到应用程序后的web浏览器。
然后输入以下命令启动应用程序:
(venv) $ python hello.py * Running on http://127.0.0.1:5000/ * Restarting with reloader
如果你输入任何其他URL,应用程序将不知道如何操作它并且将返回错误代码404给浏览器——当你访问一个不存在的网页也会得到该错误。
下面所示应用程序的增强版添加了第二个动态路由。当你访问这个URI,你应该可以看到一个个性的问候。
示例hello.py:带有动态路由的Flask应用程序
from flask import Flask app = Flask(__name__) @app.route('/') def index(): return 'Hello World!
' @app.route('/user/') def user(name): return ' Hello, %s!
' % name if __name__ == '__main__': app.run(debug=True)
建议:如果你有克隆在GitHub上的应用程序,你现在可以运行git checkout 2b来切换到这个版本的应用程序。
测试动态路由,确保服务正在运行随后访问 http://localhost:5000/user/Dave 。生成的应用程序会使用动态参数名响应一个定制的问候。尝试不同的名称,看看视图函数总是生成响应基于给定的名称。
5.1、应用程序Context和请求Context
当Flask从客户端收到一个请求,它需要提供几个可用对象给视图函数处理。request对象是个不错的例子,它封装了客户端发送的HTTP请求。
Flask视图函数访问request对象的最好方式,就是作为一个参数发送它,但这需要每个单一视图函数在应用程序中有一个额外的参数。考虑一下,如果request对象不是唯一一个视图函数需要访问完成请求的对象,事情将会变得更加复杂。
为了避免弄乱视图函数那些可能需要或不需要的参数,Flask使用context来临时确定可访问的全局对象。也多亏了context,视图函数可以写成下面这样:
from flask import request @app.route('/') def index(): user_agent = request.headers.get('User-Agent') return ' <p>Your browser is %s</p> ' % user_agent
注意,在这个视图函数中,request是如何被作为一个全局变量来使用的。现实中,request是不能作为全局变量的,如果是多线程服务器,同一时间线程作用于不同客户端的不同请求,所以每一个线程需要看到request中的不同对象。contexts使得Flask确定可访问的全局变量而不干扰其他线程。
注:线程是可以独立管理的最小指令序列。一个进程中有多个活动的线程是非常常见的,有时分享内存或文件句柄资源。多线程web服务器会启动一个线程池并从池中选择一个线程来处理每个传入的请求。
Flask有两类context:应用级context 和 请求级context。表2-1展示了这些context提供的变量。
Flask激活(或压栈)应用级context和请求级context在调度请求之前,然后删除他们当请求被处理后。当应用程序context被压入栈,线程中current_app和g变量变得可用;同样的,当请求级context被压入栈,request和session变量也同样变得可用。如果这些变量中的任何一个不是由激活的应用级或请求级context访问,会产生错误。在后面的章节会详细讨论四个context变量,所以不要担心你不理解它们的用处。
下面的Python shell会话演示了应用级context是如何工作的:
>>> from hello import app >>> from flask import current_app >>> current_app.name Traceback (most recent call last): ... RuntimeError: working outside of the application context >>> app_ctx = app.app_context() >>> app_ctx.push() >>> current_app.name 'hello' >>> app_ctx.pop()
在这个示例中,当应用级context没有激活,但是却作为有效的context被压入栈中,current_app.name报错。注意在应用程序实例中一个应用级context是如何通过调用app.app_context()来获得的。
5.2、请求调度
当一个应用程序收到客户端的请求,它需要找到响应的视图函数为之服务。对于这个任务,Flask会在应用程序的URL映射中查找请求的URL,该映射包含URLs和操作它们的视图函数。Flask通过app.route装饰器或非装饰器版本app.add_url_rule()来建立这个映射。
看一下Flask应用程序中URL映射是怎样的,你可以在Python shell中检查hello.py创建的映射。测试中,请确保你的虚拟环境是激活状态:
(venv) % python >>> from hello import app >>> app.url_map Map([<Rule '/' (HEAD, OPTIONS, GET) -> index>, <Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>, <Rule '/user/<name>' (HEAD, OPTIONS, GET) -> user>])
URL映射中所示的HEAD、OPTIONS、GET元素为request方法,由路由处理。Flask连接方法到每个路由,这样不同的请求方法发送到相同的URL可以被不同的视图函数处理。HEAD和OPTIONS方法由Flask自动管理,所以实际上可以说,在这个应用程序中URL映射的三个路由都连接到GET方法了。在第四章你将学习为路由指定不同的请求方法。
5.3、请求Hooks
有些时候在每个请求处理之前或之后执行代码是非常有用的。例如,在开始每一个请求前可能有必要创建数据库连接,或对用户请求进行验证。为了避免复制处理这些操作的代码到每一个视图函数中,Flask给你选择注册相同函数来调用,在请求被分配给视图函数之前或之后。
请求hooks由装饰器实现。下面是四个Flask支持的hooks:
(1)before_first_request:在第一个请求被处理前注册一个函数运行。
(2)before_request:在每一个请求前注册一个函数运行。
(3)after_request:如果没有未处理的异常发生,在每一个请求后注册一个函数运行。
(4)teardown_request:即使未处理的异常发生,在每一个请求后注册一个函数运行。
在请求hook函数和视图函数之间共享数据的惯用方法就是使用g全局context。例如,before_request处理程序可以从数据库加载已登录的用户并保存在g.user中。之后,当视图函数被调用,可以从那访问用户。
请求hooks的示例会在未来的章节中展示给大家,所以不用担心,
5.4、响应
当Flask调用一个视图函数,并期望它的返回值去响应该请求。大多数的响应是将简单字符串构成的HTML页面发回给客户端。
但是HTTP协议需要比字符串更多的信息作为请求的响应。一个HTTP响应中非常重要的部分是状态码,Flask默认设置200来指示请求已经成功处理。
当视图函数需要用不同的状态码响应,可以在响应文本后添加数字码作为第二个返回值。例如,下面的视图函数返回400错误状态码的请求:
@app.route('/') def index(): return '<h1>Bad Request</h1>', 400
视图函数返回的响应还可以携带第三个参数,添加一个头部字典给HTTP响应。通常很少用到,但是你可以在第十四章看到示例。
除了返回一个、两个或三个值的元组,Flask视图函数可以选择返回response对象。make_response()函数可携带一个、两个或三个参数,和视图函数返回的值一样,并返回一个response对象。有时候在视图函数中执行这个转换是非常有用的,然后使用response对象中的方法进一步配置响应。下面的示例创建response对象并设置cookie:
from flask import make_response @app.route('/') def index(): response = make_response(' <h1>This document carries a cookie!</h1> ') response.set_cookie('answer', '42') return response
有一类特殊的响应称作重定向。这类响应不包含页面文档;只是给浏览器一个新的URL去加载新的页面。重定向通常和web表单一起使用,你将在第四章学习。
重定向通常由302响应状态码注明并且重定向的URL由头部的Location给出。重定向响应可以使用三个值的返回生成,也可通过响应对象生成,但是鉴于它频繁的使用,Flask提供redirect()函数来创建这样的响应:
from flask import redirect @app.route('/') def index(): return redirect('http://www.example.com')
另一个具有中断功能的特殊响应用来错误处理。下面的示例,当URL给出的id动态参数不是一个合法的用户时返回状态码404:
from flask import abort @app.route('/user/<id>') def get_user(id): user = load_user(id) if not user: abort(404) return ' <h1>Hello, %s</h1> ' % user.name
注意终止不是指将控制权返回给调用它的函数,而是指通过抛出异常将控制权返回给web服务。
6、Flask扩展
Flask是可扩展的。它故意腾出地给重要的功能,例如数据库和用户授权,给你自由去选择最适合你的应用程序的包,或写一个自己想要的。
社区开发了非常多的扩展用于各种用途,如果这还不够,可以使用任何Python标准包和库。为了让你了解一个扩展是如何并入一个应用程序的,下面的章节给hello.py添加一个扩展,增加应用程序的命令行参数。
6.1、Flask-Script命令行选项
Flask开发,其web服务器支持一系列的启动配置选项,但是配置它们的唯一方式只有在脚本中传递参数给app.run()并调用。这不是非常的方便,理想方法是通过命令行参数传递配置选项。
Flask-Script是给你的Flask应用程序添加命令行解释的扩展。它打包了一组通用的选项,还支持自定义命令。
使用pip安装扩展:
(venv) $ pip install flask-script
下面展示了在 hello.py 应用程序中添加命令行解释的变化。
示例. hello.py:使用Flask-Script
from flask.ext.script import Manager manager = Manager(app) # ... if __name__ == '__main__': manager.run()
专为Flask开发的扩展暴露在flask.ext命名空间下。Flask-Script从flask.ext.script中导出一个名为Manager的类。
初始化这个扩展的方法和其他许多扩展一样:主类实例的初始化是通过将应用程序实例作为参数传递给构造函数实现的。创建的对象适当的用于每一个扩展。在这个示例中,服务器启动通过manager.run()来路由,且命令行在这被解析。
建议:如果你有克隆在GitHub上的应用程序,你现在可以运行git checkout 2c来切换到这个版本的应用程序。
因为这些变化,应用程序获得一组基本的命令行选项。运行hello.py显示可用信息:
$ python hello.py
usage: hello.py [-h] {shell, runserver} ... positional arguments: {shell, runserver} shell 在Flask应用程序上下文的内部运行一个Python Shell。 runserver 运行Flask开发服务器,例如:app.run() optional arguments: -h, --help 显示这个帮助信息并退出
shell命令用于在应用程序上下文中启动一个Python shell会话。你可以使用这个会话去运行维护任务,或测试,或调试错误。
runserver命令,就像它的名称一样,启动web服务。运行python hello.py runserver在调试模式下启动web服务,还有更多的选项:
(venv) $ python hello.py runserver --help usage: hello.py runserver [-h] [-t HOST] [-p PORT] [--threaded] [--processes PROCESSES] [--passthrough-errors] [-d] [-r]
运行Flask开发服务器,例如:app.run()
optional arguments: -h, --help 显示这个帮助信息并退出 -t HOST, --host HOST -p PORT, --port PORT --threaded --processes PROCESSES --passthrough-errors -d, --no-debug -r, --no-reload
--host参数是一个非常有用的选项,因为它能告诉web服务器监听哪个网络接口的客户端连接。默认,Flask开发的web服务器监听localhost的连接,所以只有来自内部计算机运行的服务器可以接收。下面的命令使得web服务器监听公网接口,其他网络上的计算机可以连接:
(venv) $ python hello.py runserver --host 0.0.0.0 * Running on http://0.0.0.0:5000/ * Restarting with reload
现在web服务器应该可以从网络中的任何一台计算机访问 http://a.b.c.d:5000 ,“a.b.c.d”是运行服务的计算机的外部IP地址。
更多用Python の Flask フレームワークを使用して最初の Web アプリケーションを構築する相关文章请关注PHP中文网!