Blueprintを使ったアプリケーションのモジュール化 Modular Applications with Blueprints

Changelog

バージョン 0.7 で追加.

Flaskはアプリケーションの部品作成、およびアプリケーション内またはアプリケーションを横断した共通パターンのサポートにblueprint(青写真)という考え方を使用します。blueprintは大きなアプリケーションの動き方を非常にシンプルにし、Flask拡張がアプリケーションへ操作(operation)を登録するための中心手段を提供します。BlueprintオブジェクトはFlaskと似た働きをしますが、実際にはアプリケーションではありません。むしろ、それはアプリケーションをどのように建設もしくは拡張するかのblueprint(青写真)です。 Flask uses a concept of *blueprints* for making application components and supporting common patterns within an application or across applications. Blueprints can greatly simplify how large applications work and provide a central means for Flask extensions to register operations on applications. A :class:`Blueprint` object works similarly to a :class:`Flask` application object, but it is not actually an application. Rather it is a *blueprint* of how to construct or extend an application.

なぜBlueprintsか? Why Blueprints?

Flaskのblueprintは以下のような場面を意図しています: Blueprints in Flask are intended for these cases:

  • アプリケーションをblueprintのセットに分解。これは大きいアプリケーションにとって理想的です; プロジェクトはアプリケーションのオブジェクトをインスタンス化でき、いくつかのFlask拡張を初期化でき、そしてblueprintの集合を登録できます。 Factor an application into a set of blueprints. This is ideal for larger applications; a project could instantiate an application object, initialize several extensions, and register a collection of blueprints.

  • URLのプレフィックスおよび/またはサブドメインに対してblueprintをアプリケーション(Flaskのインスタンス)に登録。そのURLのプレフィックス/サブドメインの中にあるパラメータは、そのblueprint内のすべてのview関数にわたる、viewへの(既定値を伴った)共通の引数になります(訳注: 後の方にある「最初のBlueprint」の節のサンプルコードや「Blueprintの登録」の節の記載内容に対応した処理のことだと思います)。 Register a blueprint on an application at a URL prefix and/or subdomain. Parameters in the URL prefix/subdomain become common view arguments (with defaults) across all view functions in the blueprint.

  • 違うURLルールを使ってアプリケーション(Flaskのインスタンス)へblueprintを複数回登録(訳注: 同じblueprintを異なる複数のURLに登録するような意味合い。ここでのURLルールは、URLとエンドポイントとの対応関係などを定義したもので、通常はRuleとして処理されるものだと思います)。 Register a blueprint multiple times on an application with different URL rules.

  • テンプレートのfilter(訳注: Jinjaテンプレートエンジンの機能のひとつ)、静的ファイル、テンプレート、その他のユーティリティを、blueprintを通して提供。blueprintではアプリケーション(WSGIアプリのインタフェースまたはFlaskインスタンスのような意味合い)やview関数の実装は必須ではありません。 Provide template filters, static files, templates, and other utilities through blueprints. A blueprint does not have to implement applications or view functions.

  • Flask拡張を初期化するとき、以上のいずれかの理由のために、アプリケーション(Flaskインスタンス)へblueprintを登録。 Register a blueprint on an application for any of these cases when initializing a Flask extension.

Flaskのblueprintは実際にはアプリケーション(Flaskのサブクラスやインスタンス、またはWSGIアプリのような意味合い)ではないので、着脱可能(pluggable、プラグインのように後から追加・削除できるような意味合い)なappではありません--それは、アプリケーションに登録可能な操作(operation)であり、複数回登録することすら可能です。なぜ(わざわざblueprintを使わず)複数のアプリケーションのオブジェクト(Flaskインスタンス)を持たないのでしょうか?それをすることも可能です(アプリケーションの振り分け(Application Dispatching)を見てください)が、(そのときは)アプリケーションは分かれた設定を持ち、WSGIの階層で管理されます。 A blueprint in Flask is not a pluggable app because it is not actually an application -- it's a set of operations which can be registered on an application, even multiple times. Why not have multiple application objects? You can do that (see :doc:`/patterns/appdispatch`), but your applications will have separate configs and will be managed at the WSGI layer.

blueprintは、代わりに(WSGIアプリケーションである)Flaskのレベルでの分離を提供し、(blueprintが)アプリケーション(Flaskインスタンス)の設定は共有し、そして必要に応じて(blueprintが)登録されることでアプリケーションのオブジェクト(Flaskインスタンス)を変更できます。マイナス面は、一度アプリケーションが作成されると、アプリケーションのオブジェクト全体を破壊しない限りblueprintの登録を外せないことです。 Blueprints instead provide separation at the Flask level, share application config, and can change an application object as necessary with being registered. The downside is that you cannot unregister a blueprint once an application was created without having to destroy the whole application object.

Blueprintのコンセプト The Concept of Blueprints

blueprintの基本コンセプトは、(blueprintが)アプリケーション(Flaskインスタンス)に登録された場合には実行する操作(operation)を記録しておくことです。Flaskは、リクエストを振り分ける(dispatching)ときや、あるエンドポイントから別のエンドポイントへのURLを生成するときに、view関数をblueprintと関連付けます(訳注: URLの生成については、このドキュメントの後半にある、URLの構築(Building URLs)に関する処理を指していると思います)。 The basic concept of blueprints is that they record operations to execute when registered on an application. Flask associates view functions with blueprints when dispatching requests and generating URLs from one endpoint to another.

最初のBlueprint My First Blueprint

以下は、とても基本的なblueprintがどのようになるかを示しています。以下のケースでは、静的なテンプレートを表示(rendering)するblueprintを実装しようとしています: This is what a very basic blueprint looks like. In this case we want to implement a blueprint that does simple rendering of static templates::

from flask import Blueprint, render_template, abort
from jinja2 import TemplateNotFound

simple_page = Blueprint('simple_page', __name__,
                        template_folder='templates')

@simple_page.route('/', defaults={'page': 'index'})
@simple_page.route('/<page>')
def show(page):
    try:
        return render_template(f'pages/{page}.html')
    except TemplateNotFound:
        abort(404)

@simple_page.routeデコレータを利用して関数を結び付け(bind)するとき、blueprintは、後で(blueprintがFlaskインスタンスに)登録されるときにはshow関数をアプリケーション(Flaskインスタンス)へ登録するという意図を記録しておきます。さらに、blueprintは関数のエンドポイントの先頭へ、Blueprintクラスのコンストラクタで与えられたblueprintの名前(この例ではsimple_page)を付加します。blueprintの名前は、URLは変更せず、エンドポイントだけを変更します(訳注: ここでは、simple_page.showのように、エンドポイントの最初にはblueprint名を付け、対応するURLパスの「'/'」や「'/<paga>'」の最初にはblueprint名を付けないでURLルールでの対応付けが作成される、という意味合い)。 When you bind a function with the help of the ``@simple_page.route`` decorator, the blueprint will record the intention of registering the function ``show`` on the application when it's later registered. Additionally it will prefix the endpoint of the function with the name of the blueprint which was given to the :class:`Blueprint` constructor (in this case also ``simple_page``). The blueprint's name does not modify the URL, only the endpoint.

Blueprintの登録 Registering Blueprints

ではどうやってblueprintを登録するのでしょうか?以下のようにします: So how do you register that blueprint? Like this::

from flask import Flask
from yourapplication.simple_page import simple_page

app = Flask(__name__)
app.register_blueprint(simple_page)

もしアプリケーション(Flaskインスタンス)に登録されたルール(訳注: URLとエンドポイントとの対応関係などを定義したもの、通常はWerkzeugのRuleクラスのオブジェクト)をチェックすれば、以下の内容を見つけ出せるでしょう: If you check the rules registered on the application, you will find these::

>>> app.url_map
Map([<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
 <Rule '/<page>' (HEAD, OPTIONS, GET) -> simple_page.show>,
 <Rule '/' (HEAD, OPTIONS, GET) -> simple_page.show>])

最初のひとつは明らかにアプリケーション自身が静的ファイルに対してしたものです。他の2つは、simple_pageのblueprintのshow関数に対するものです。見て分かるように、それら(show関数のエンドポイント)はblueprint名も最初に付けられていて(prefix)、ドット(.)で(blueprint名とshow関数名とが)分けられています。 The first one is obviously from the application itself for the static files. The other two are for the `show` function of the ``simple_page`` blueprint. As you can see, they are also prefixed with the name of the blueprint and separated by a dot (``.``).

しかしながら、blueprintは違う場所にも設置(mount)できます: Blueprints however can also be mounted at different locations::

app.register_blueprint(simple_page, url_prefix='/pages')

そして思ったとおりに、生成されたルールは以下のようになります: And sure enough, these are the generated rules::

>>> app.url_map
Map([<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
 <Rule '/pages/<page>' (HEAD, OPTIONS, GET) -> simple_page.show>,
 <Rule '/pages/' (HEAD, OPTIONS, GET) -> simple_page.show>])

この上にblueprintを複数回登録できますが、blueprintの中には適切に対応しないものがあるかもしれません。事実、blueprintが2回以上設置(mount)できるかは、blueprintがどのように実装されているかに依存します。 On top of that you can register blueprints multiple times though not every blueprint might respond properly to that. In fact it depends on how the blueprint is implemented if it can be mounted more than once.

入れ子にしたBlueprint(Nesting Blueprints) Nesting Blueprints

あるblueprintを別のblueprintに登録できます。 It is possible to register a blueprint on another blueprint.

parent = Blueprint('parent', __name__, url_prefix='/parent')
child = Blueprint('child', __name__, url_prefix='/child')
parent.register_blueprint(child)
app.register_blueprint(parent)

子供のblueprintは自分の名前の接頭辞(prefix)として親の名前を獲得し、子供のURLの先頭には親のURLの接頭辞が付けられます。 The child blueprint will gain the parent's name as a prefix to its name, and child URLs will be prefixed with the parent's URL prefix.

url_for('parent.child.create')
/parent/child/create

親に登録されたblueprint固有のbefore request関数などは、子供用の引き金(trigger)になります。もし子供が与えられたエラーを処理できるerror handlerを持たない場合は、親のerror handlerが試されます。 Blueprint-specific before request functions, etc. registered with the parent will trigger for the child. If a child does not have an error handler that can handle a given exception, the parent's will be tried.

Blueprintのリソース Blueprint Resources

blueprintはリソースの提供もできます。ときには、ただblueprintが提供するリソースのためだけに、blueprintを導入したいときがあるかもしれません。 Blueprints can provide resources as well. Sometimes you might want to introduce a blueprint only for the resources it provides.

Blueprintのリソースフォルダ Blueprint Resource Folder

通常のアプリケーションのように、blueprintはフォルダの中に含まれていると想定されています。複数のblueprintを同じフォルダから由来させることも可能ですが、そうする必要はなく、そしてそれは普通は推奨されません。 Like for regular applications, blueprints are considered to be contained in a folder. While multiple blueprints can originate from the same folder, it does not have to be the case and it's usually not recommended.

そのフォルダはBlueprintクラスへの2番目の引数から推測され、普通は__name__になります。この引数は、どの論理Pythonモジュールまたは論理Pythonパッケージがblueprintに対応するかを指定します(訳注: Pythonでは大まかには、モジュールは単一の.pyファイル、パッケージはモジュールなどを格納するフォルダのことを言います)。もしそれがPythonパッケージを指していれば、そのパッケージ(それはファイルシステム上はフォルダになります)がリソースフォルダになります。もしそれがモジュールであれば、そのモジュールを含んでいるパッケージがリソースフォルダになります。リソースフォルダが何か確認するためには、Blueprint.root_pathプロパティにアクセスできます: The folder is inferred from the second argument to :class:`Blueprint` which is usually `__name__`. This argument specifies what logical Python module or package corresponds to the blueprint. If it points to an actual Python package that package (which is a folder on the filesystem) is the resource folder. If it's a module, the package the module is contained in will be the resource folder. You can access the :attr:`Blueprint.root_path` property to see what the resource folder is::

>>> simple_page.root_path
'/Users/username/TestProject/yourapplication'

このフォルダから手早くリソース(訳注: 原文は「sources」ですが、おそらく誤記)を開くには、open_resource()関数を使えます: To quickly open sources from this folder you can use the :meth:`~Blueprint.open_resource` function::

with simple_page.open_resource('static/style.css') as f:
    code = f.read()

静的ファイル Static Files

blueprintは、static_folder引数を使いファイルシステム上のフォルダへのパスを提供することで、静的ファイルのフォルダを公開することができます。それは絶対パスか、blueprintの場所からの相対パスのどちらかです: A blueprint can expose a folder with static files by providing the path to the folder on the filesystem with the ``static_folder`` argument. It is either an absolute path or relative to the blueprint's location::

admin = Blueprint('admin', __name__, static_folder='static')

標準設定では、パスの最も右側の部分が、web上で公開される場所になります。これはstatic_url_path引数で変えることができます。上の例ではそのフォルダがstaticで呼び出されている(訳注: static_folder引数に「static」が指定されている)ため、これはblueprintのurl_prefix + /staticでアクセスできます。もしもblueprintのプレフィックスが/adminであれば、staticのURLは/admin/staticになります。 By default the rightmost part of the path is where it is exposed on the web. This can be changed with the ``static_url_path`` argument. Because the folder is called ``static`` here it will be available at the ``url_prefix`` of the blueprint + ``/static``. If the blueprint has the prefix ``/admin``, the static URL will be ``/admin/static``.

このエンドポイントはblueprint_name.staticになります。そのURLはアプリケーション(Flaskインスタンス)のstaticのフォルダと同様に、url_for()を使って生成可能です: The endpoint is named ``blueprint_name.static``. You can generate URLs to it with :func:`url_for` like you would with the static folder of the application::

url_for('admin.static', filename='style.css')

しかしながら、もしこのblueprintがurl_prefixを持たない場合は、このblueprintのstaticのフォルダはアクセス不可能になります。これは、この場合には(blueprintの静的ファイルのフォルダの)URLを/staticにしようとして、それに対してはアプリケーション(Flaskインスタンス)の/staticの経路(route)が優先されるためです。テンプレートのフォルダと異なり、blueprintのstaticのフォルダは、もしアプリケーション(Flaskインスタンス)のstaticのフォルダが存在しない場合でも使用されません。 However, if the blueprint does not have a ``url_prefix``, it is not possible to access the blueprint's static folder. This is because the URL would be ``/static`` in this case, and the application's ``/static`` route takes precedence. Unlike template folders, blueprint static folders are not searched if the file does not exist in the application static folder.

テンプレート Templates

もしblueprintでテンプレートを公開したい場合は、Blueprintのコンストラクタにtemplate_folder引数を提供すれば可能です: If you want the blueprint to expose templates you can do that by providing the `template_folder` parameter to the :class:`Blueprint` constructor::

admin = Blueprint('admin', __name__, template_folder='templates')

テンプレートに対しては(訳注: 原文は「For static files」ですが、おそらく誤記)、パスは絶対パスか、blueprintのリソースフォルダからの相対パスが可能です。 For static files, the path can be absolute or relative to the blueprint resource folder.

(blueprintの)テンプレートのフォルダは、テンプレートを探すパスに追加されますが、実際のアプリケーション(Flaskインスタンス)のテンプレートのフォルダよりは優先度が低くなります。このやり方により、blueprintが提供したテンプレートは、実際のアプリケーションの中で容易に上書きが可能になります。これは、もしblueprintのテンプレートが事故で上書きされることを望まない場合には、他のblueprintや実際のアプリケーションのテンプレートは、同じ相対パスを持たないようにしなければならないことを意味します。複数のblueprintが同じ相対パスのテンプレートを提供するときは、最初に登録されたblueprintが他よりも優先されます。 The template folder is added to the search path of templates but with a lower priority than the actual application's template folder. That way you can easily override templates that a blueprint provides in the actual application. This also means that if you don't want a blueprint template to be accidentally overridden, make sure that no other blueprint or actual application template has the same relative path. When multiple blueprints provide the same relative template path the first blueprint registered takes precedence over the others.

従って、もしyourapplication/adminフォルダにblueprintがあり、'admin/index.html'テンプレートを表示しようとしており、templatestemplate_folderとして提供していたときには、yourapplication/admin/templates/admin/index.htmlのようなファイルを作成する必要があります。ここで追加のadminフォルダがある理由は、このテンプレートが、実際のアプリケーション(Flaskインスタンス)のテンプレートのフォルダにあるindex.htmlテンプレートによって上書きされることを防ぐためです。 So if you have a blueprint in the folder ``yourapplication/admin`` and you want to render the template ``'admin/index.html'`` and you have provided ``templates`` as a `template_folder` you will have to create a file like this: :file:`yourapplication/admin/templates/admin/index.html`. The reason for the extra ``admin`` folder is to avoid getting our template overridden by a template named ``index.html`` in the actual application template folder.

さらに繰り返します: もしもadminというblueprintがあり、そのblueprintに固有のindex.htmlというテンプレートを表示したい場合は、最も良いアイデアは以下のようにテンプレートをレイアウトすることです: To further reiterate this: if you have a blueprint named ``admin`` and you want to render a template called :file:`index.html` which is specific to this blueprint, the best idea is to lay out your templates like this::

yourpackage/
    blueprints/
        admin/
            templates/
                admin/
                    index.html
            __init__.py

そして、テンプレートを表示したいときに、テンプレートを探すときのテンプレート名にadmin/index.htmlを使用します。もしも正しいテンプレートの読み込みに問題が生じたときは、設定値EXPLAIN_TEMPLATE_LOADINGを有効にして、render_templateを呼び出すたびにテンプレートを見つけるために実行しているステップを表示するよう、Flaskに指示してください。 And then when you want to render the template, use :file:`admin/index.html` as the name to look up the template by. If you encounter problems loading the correct templates enable the ``EXPLAIN_TEMPLATE_LOADING`` config variable which will instruct Flask to print out the steps it goes through to locate templates on every ``render_template`` call.

URLの構築 Building URLs

もしあるページから別のページへリンクしたいときは、通常時にするようにurl_for()関数を使うことができ、blueprintの名前とドット(.)をURLのエンドポイントの前に付けるだけです: If you want to link from one page to another you can use the :func:`url_for` function just like you normally would do just that you prefix the URL endpoint with the name of the blueprint and a dot (``.``)::

url_for('admin.index')

さらに、もしblueprintのview関数もしくは変換(render)されたテンプレートの中で、同じblueprintの別のエンドポイントへリンクしたいときは、ドットだけをエンドポイントの前につける相対リダイレクトを使えます: Additionally if you are in a view function of a blueprint or a rendered template and you want to link to another endpoint of the same blueprint, you can use relative redirects by prefixing the endpoint with a dot only::

url_for('.index')

上の例は、そのときのリクエストがadminのblueprintにある別のエンドポイントへ振り分けられていたとき、admin.indexへリンクします。 This will link to ``admin.index`` for instance in case the current request was dispatched to any other admin blueprint endpoint.

ブループリントのエラー処理(Blueprint Error Handlers) Blueprint Error Handlers

Flaskアプリケーションのオブジェクトと同様に、blueprintはerrorhandlerデコレータをサポートしており、従ってblueprint固有のエラーページを作成するのは簡単です。 Blueprints support the ``errorhandler`` decorator just like the :class:`Flask` application object, so it is easy to make Blueprint-specific custom error pages.

「404 Page Not Found」例外に対する例がこちらです: Here is an example for a "404 Page Not Found" exception::

@simple_page.errorhandler(404)
def page_not_found(e):
    return render_template('pages/404.html')

殆どのerrorhandlerは、予想したとおりに動きます; しかし、404と405の例外の処理に関して注意点があります(訳注: HTTPレスポンスのステータスコードを指していて、404は「Not Found」、405は「Method Not Allowed」になります)。これらのerrorhandlerが起動されるのは、そのblueprint内の他のview関数の中で適切なraise文またはabortを呼んだときだけです; それらは、例えば不正なURLへのアクセスからは起動されません。これは、blueprintはあるURL空間を「所有」しておらず、従ってアプリケーションのインスタンスは不正なURLを渡されたとしても、どのblueprintのerrorhandlerを実行すればよいかを知るすべがないためです。もしURLプレフィックスに基づいて、これらのエラーに対して異なる処理戦略を実行したいときは、それらはアプリケーションのレベルでrequestの代理(proxy)のオブジェクト(訳注: 例えば「from flask import request」でグローバルにアクセスできるrequestオブジェクト)を使って定義させることができます: Most errorhandlers will simply work as expected; however, there is a caveat concerning handlers for 404 and 405 exceptions. These errorhandlers are only invoked from an appropriate ``raise`` statement or a call to ``abort`` in another of the blueprint's view functions; they are not invoked by, e.g., an invalid URL access. This is because the blueprint does not "own" a certain URL space, so the application instance has no way of knowing which blueprint error handler it should run if given an invalid URL. If you would like to execute different handling strategies for these errors based on URL prefixes, they may be defined at the application level using the ``request`` proxy object::

@app.errorhandler(404)
@app.errorhandler(405)
def _handle_api_error(ex):
    if request.path.startswith('/api/'):
        return jsonify(error=str(ex)), ex.code
    else:
        return ex

アプリケーションのエラーの処理を確認してください。 See :doc:`/errorhandling`.