7

次のような 3 つの変数コンポーネントを使用して URL ルールを定義したいと考えています。

@app.route('/<var_1>/<var_2>/<var3>/')

しかし、開発サーバーは、静的ファイルとの照合を試みる前に、そのようなルールを評価することがわかりました。だから、次のようなもの:

/static/images/img.jpg

組み込みの静的ファイル ハンドラーに転送されるのではなく、私の URL ルールによってキャッチされます。最初に開発サーバーを強制的に静的ファイルに一致させる方法はありますか?

PS これは、ルールに 3 つ以上の変数コンポーネントがある場合にのみ問題になります。

4

4 に答える 4

18

これは werkzeug ルート最適化機能です。およびを参照Map.addしてください。Map.updateRule.match_compare_key

def match_compare_key(self):
    """The match compare key for sorting.

    Current implementation:

    1. rules without any arguments come first for performance
    reasons only as we expect them to match faster and some
    common ones usually don't have any arguments (index pages etc.)
    2. The more complex rules come first so the second argument is the
    negative length of the number of weights.
    3. lastly we order by the actual weights.

    :internal:
    """
    return bool(self.arguments), -len(self._weights), self._weights

- 現在のself.arguments引数self._weights- パスの深さ。

'/<var_1>/<var_2>/<var3>/'があるから(True, -3, [(1, 100), (1, 100), (1, 100)])です。(1, 100)- 最大長が 100 のデフォルトの文字列引数があります。

'/static/<path:filename>'があるから(True, -2, [(0, -6), (1, 200)])です。(0, 1)- パス非引数文字列長static(1, 200)- パス文字列引数最大長 200があります。

Mapしたがって、独自の実装を設定しFlask.url_mapたり、マップルールの優先度を設定したりする美しい方法は見つかりません。ソリューション:

  1. Flaskとしてアプリケーションをセットアップしますapp = Flask(static_path='static', static_url_path='/more/then/your/max/variables/path/depth/static')
  2. に変更@app.route('/<var_1>/<var_2>/<var3>/')@app.route('/prefix/<var_1>/<var_2>/<var3>/')ます。
  3. 独自のコンバーターを追加し、として使用し@app.route('/<no_static:var_1>/<var_2>/<var3>/')ます。
  4. インポートwerkzeug.routing、独自のマップ実装の作成、werkzeug.routing.Map独自の実装への変更、インポートflask
  5. サーバーを本番環境と同様に使用します。
于 2013-06-17T11:36:11.527 に答える
7

したがって、tbicr指摘したように、この動作は Werkzeug の奥深くに設定されており、Flask からそれを処理するエレガントな方法は実際にはありません。私が思いつく最善の回避策は次のとおりです。

次のような補完的な静的ファイル ハンドラーを定義します。

@app.route('/static/<subdir>/<path:filename>/')
def static_subdir(subdir=None, filename=None):

    directory = app.config['STATIC_FOLDER'] + subdir
    return send_from_directory(directory, filename)

ここでapp.config['STATIC_FOLDER']は、アプリケーションを実行しているマシンの静的フォルダーへのフル パスを示します。

さて、このハンドラーは のようなものをキャッチ/static/images/img.jpgし、私のビューには 3 つの変数コンポーネントだけが残されます。

于 2013-06-17T14:01:32.017 に答える
3

これを回避する 1 つの方法は、登録されたルールのmatch_compare_key()メソッドを偽装して、ルールの並べ替えアルゴリズムをごまかすことです。このハックは、app.route()(Flask オブジェクト) に直接登録されたルートでのみ機能し、ブループリントでは機能しないことに注意してください。ブループリントのルートは、ブループリントがメイン アプリに登録されたときにのみグローバル URL マップに追加されるため、生成されたルールを変更するのは困難です。

# an ordinary route
@app.route('/<var1>/<var2>/<var3>')
def some_view(var1, var2, var3):
    pass

# let's find the rule that was just generated
rule = app.url_map._rules[-1]

# we create some comparison keys:
# increase probability that the rule will be near or at the top
top_compare_key = False, -100, [(-2, 0)]
# increase probability that the rule will be near or at the bottom 
bottom_compare_key = True, 100, [(2, 0)]

# rig rule.match_compare_key() to return the spoofed compare_key
rule.match_compare_key = lambda: top_compare_key

この場合、結果のスプーフィングされた関数はルール オブジェクトにバインドされないことに注意してください。したがって、 を呼び出すと、関数は引数rule.match_compare_key()を受け取りません。self関数を適切にバインドする場合は、代わりに次のようにします。

spoof = lambda self: top_compare_key
rule.match_compare_key = spoof.__get__(rule, type(rule))

上記をデコレータで一般化できます

def weighted_route(*args, **kwargs):
    def decorator(view_func):
        compare_key = kwargs.pop('compare_key', None)
        # register view_func with route
        app.route(*args, **kwargs)(view_func)

        if compare_key is not None:
            rule = app.url_map._rules[-1]
            rule.match_compare_key = lambda: compare_key

        return view_func
    return decorator

# can be used like @app.route(). To weight the rule, just provide
# the `compare_key` param.
@weighted_route('/<var1>/<var2>/<var3>', compare_key=bottom_compare_key)
def some_view(var1, var2, var3):
    pass

コンテキストマネージャーとして実装された同じハック。

import contextlib

@contextlib.contextmanager
def weighted_route(compare_key=None):
    yield
    if compare_key is not None:
        rule = app.url_map._rules[-1]
        rule.match_compare_key = lambda: compare_key

# and to use

with weighted_route(compare_key):
    @app.route('/<var1>/<var2>/<var3>')
    def some_view(var1, var2, var3):
        pass
于 2014-02-25T08:22:37.727 に答える
0

@tbicr (提案 #3) に触発された回答を提供したいと思いました。これは、他のいくつかのソリューションよりも少しクリーンなようです。

from werkzeug.routing import BaseConverter
class NoStaticConverter(BaseConverter):
    regex = '[^/]+(?<!/static)'
app.url_map.converters['nostatic'] = NoStaticConverter
app.add_url_rule('/<nostatic:page>/<page2>/<page3>/<page4>/',view_func=Main.as_view('level4'),methods=["GET"])

pagepage2page3、およびpage4がクラスに渡されますMain(テンプレートのレンダリングに使用するものは示されていません)。

注意すべきことの 1 つは、これはhttp://127.0.0.1:5000/a/b/c//の形式の URL にスラッシュが追加されている場合には機能しないようです。これは不正な URL で、flask が自動的に書き換えて余分なスラッシュを削除すると思っていましたが、そうではありません。これは静的ファイルとの競合に固有の問題ではないようですが、言及する価値があると思いました。

于 2022-01-18T19:29:38.523 に答える