14

アプリの呼び出し可能オブジェクト、WSGI サーバー、Flask の循環インポートに関する質問

私は(おそらく)混乱しています。app-factories から Flask / WSGI アプリを安全に作成し、WSGI サーバーで簡単に使用できるようにしたいと考えています。

tl;dr

  1. initのインポート時にアプリを作成するのを安全に回避し(推奨)、後で作成することはできますか (つまり、ファクトリ メソッドを使用)

  2. そのアプリを WSGI サーバーでうまく動作させるにはどうすればよいですか? 特に、構成やその他の設定をENVから取得していないときに渡します

例えば::

def make_app(configdict, appname):
    app = Flask(appname)
    app.config.update(configdict)
    init_db(configdict)
    set_app_in_global_namespace(app)

    #importing now will allow from pkg import app        
    from mypackage import views

    return app

上記の「ファクトリー」を使用したいのは、テストなどの構成を簡単に制御したいからです。

次に、アプリケーションを WSGI サーバーに提供する wsgi.py モジュールを作成したいと考えています。

したがって、最終的には次のようになります

初期化.py::

app = None

def make_app(configdict, appname):
    flaskapp = Flask(appname)
    flaskapp.config.update(configdict)
    init_db(configdict)

    global app
    app = flaskapp    

    #importing now will allow from pkg import app        
    from mypackage import views

    return flaskapp

wsgi.py::

from mypackage import app

app = make_app(configfromsomewhere, "myname")

uWSGI::

uwsgi --module=mypackage.wsgi:app

しかし、それでも wsgi.py は wsgi.py --settings=x --host=10.0.0.1 のように呼び出すことができるものではないため、構成を に渡す方法がよくわかりませ

私が尋ねているのは、これは... OK...と思われる一方で、少し厄介でもあるからです。

すべてがENVにあったとき、生活は楽になりました。

だけでなく、次のこともできます。

では、app-factory を使用する上で危険なことは何ですか?

here <http://flask.pocoo.org/docs/patterns/packages>_ に与えられたアドバイスは::

1. the Flask application object creation has to be in the
__init__.py file. That way each module can import it safely and
the __name__ variable will resolve to the correct package.

2. all the view functions (the ones with a route() decorator on
  top) have to be imported in the __init__.py file. Not the object
  itself, but the module it is in. Import the view module after
  the application object is created.

re: 2.、明らかに、ルート デコレータはインスタンス化されたアプリから特定の機能を期待しており、それらなしでは機能しません。それはいいです。

re: 1.、OK、正しい名前が必要です。しかし、何が安全ではないのでしょうか? なぜ?初期化されていない場合、アプリをインポートして使用するのは安全ではありませんか? 壊れますが、危険ではありません。それは非常に自慢のスレッドローカルですか?おそらく。しかし、ランダムなモジュールから無作為にアプリのインスタンスを抜き取っている場合、問題が発生することが予想されます。

含意 - ビュー以外からアプリ オブジェクトを参照することはありません - 基本的に、モジュール化を適切かつ緊密に保ち、辞書、エラー オブジェクト、さらには WebObs を渡します。

http://flask.pocoo.org/docs/patterns/appdispatch http://flask.pocoo.org/docs/deploying/#deployment http://flask.pocoo.org/docs/patterns/packages/#larger-アプリケーション http://flask.pocoo.org/docs/becomingbig

4

1 に答える 1

28

Flaskのドキュメントによると、アプリケーションファクトリは次の理由で優れています。

  1. テスト。さまざまな設定でアプリケーションのインスタンスを作成して、すべてのケースをテストできます。

  2. 複数のインスタンス。同じアプリケーションの異なるバージョンを実行したいとします。もちろん、Webサーバーに異なる構成の複数のインスタンスを設定することもできますが、ファクトリを使用する場合は、同じアプリケーションプロセスで同じアプリケーションの複数のインスタンスを実行できるので便利です。

ただし、ドキュメントの「その他のテストの秘訣」セクションに記載されているように、アプリケーションファクトリを使用している場合は、関数が自動的に呼び出さbefore_request()after_request()ません。

次の段落では、uWSGIアプリケーションサーバーとnginxでアプリケーションファクトリパターンをどのように使用しているかを示します(これらのみを使用しましたが、別のサーバーでの構成を支援することができます)。

アプリケーションファクトリ

したがって、アプリケーションがyourapplicationフォルダー内にあり、その中に__init__.pyファイルがあるとします。

import os
from flask import Flask

def create_app(cfg=None):
    app = Flask(__name__)

    load_config(app, cfg)

    # import all route modules
    # and register blueprints

    return app

def load_config(app, cfg):
    # Load a default configuration file
    app.config.from_pyfile('config/default.cfg')

    # If cfg is empty try to load config file from environment variable
    if cfg is None and 'YOURAPPLICATION_CFG' in os.environ:
        cfg = os.environ['YOURAPPLICATION_CFG']

    if cfg is not None:
        app.config.from_pyfile(cfg)

次に、アプリのインスタンスを作成するためのファイルが必要です。

from yourapplication import create_app

app = create_app()

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

上記のコードでは、構成ファイルへのパスが設定された環境変数があると想定していますが、次のように、ファクトリへの構成パスを指定できます。

app = create_app('config/prod.cfg')

または、環境と対応する構成ファイルを含む辞書のようなものを作成することもできます。

CONFIG_FILES = {'development': 'config/development.cfg',
                'test'       : 'config/test.cfg',
                'production' : 'config/production.cfg' }

この場合、load_config関数は次のようになります。

def load_config(app, env):
    app.config.from_pyfile('config/default.cfg')

    var = "YOURAPPLICATION_ENV"
    if env is None and var in os.environ:
        env = os.environ[var]

    if env in CONFIG_FILES:
        app.config.from_pyfile(CONFIG_FILES[env])

NginxとuWSGI

nginxの構成ファイルの例を次に示します。

server {
    listen             80;
    server_name        yourapplication.com;
    access_log         /var/www/yourapplication/logs/access.log;
    error_log          /var/www/yourapplication/logs/error.log;

    location / {
        try_files $uri @flask;
    }

    location @flask {
        include        uwsgi_params;
        uwsgi_pass     unix:/tmp/yourapplication.sock;

        # /env is the virtualenv directory
        uwsgi_param    UWSGI_PYHOME                /var/www/yourapplication/env;

        # the path where the module run is located
        uwsgi_param    UWSGI_CHDIR                 /var/www/yourapplication;

        # the name of the module to be called
        uwsgi_param    UWSGI_MODULE                run;

        # the variable declared in the run module, an instance of Flask
        uwsgi_param    UWSGI_CALLABLE              app;
    }
}

また、uWSGI構成ファイルは次のようになります。

[uwsgi]
plugins=python
vhost=true
socket=/tmp/yourapplication.sock
env = YOURAPPLICATION_ENV=production
logto = /var/www/yourapplication/logs/uwsgi.log

使用方法before_request()after_request()

これらの関数の問題は、他のモジュールでそれらを呼び出している場合、アプリケーションがインスタンス化される前にそれらのモジュールをインポートできないことです。繰り返しになりますが、ドキュメントにはそれについて何か言うことがあります:

欠点は、インポート時にブループリントでアプリケーションオブジェクトを使用できないことです。ただし、リクエスト内から使用できます。構成を使用してアプリケーションにアクセスするにはどうすればよいですか?current_appを使用します:

from flask import current_app, Blueprint, render_template
admin = Blueprint('admin', __name__, url_prefix='/admin')

@admin.route('/')
def index():
    return render_template(current_app.config['INDEX_TEMPLATE'])

または、拡張機能の作成を検討することもできます。クラス拡張機能は作成後にのみFlaskインスタンスを使用するため、Flaskの既存のインスタンスなしでクラスをインポートできます。

于 2012-12-06T23:16:59.903 に答える