Flaskで動的にrouteを追加する
forループを使ってrouteを追加する際のめもです。
(ついでに The Hitchhiker’s Guide to Python の Common Gotchas 1のメモも追記しています。)
最小限の最小限のアプリケーション2を準備します。
=
return
新しく/popcornと/creamsoadの2つのルートを追加することを考えます。
一般的な方法
それぞれ個別で定義する方法です。 route()3 デコレーターを使うと以下のように追加することになります。
return
return
この方法でも特に問題はありませんが、単純だったり、重複的な場合は一気に作成したくなる場合があります。
loopを使って追加する
forのloop内でview関数を定義し、add_url_rule()4 を使って追加していきます。
=
return
=
実際にapp.run()で起動させ、httpコマンド 5 を使用してアクセスできるか確認します。
; charset=utf-8
/popcornへアクセスできることが確認できました。
以下ではおまけとしてクロージャの遅延評対策とエンドポイントについて記載しています。
Late Binding Closures
上記例では、add_url_ruleでview関数を登録する際にfunctools.partial 6 を使用しています。
これは Late Binding Closures (クロージャの遅延評価)を掻い潜る(?)ために必要です。
もしpartialを使用しない場合は以下のようになります。
=
return
s.items()で与えられたvalueをtemp_funcの引数ではなくそのまま使用しています。
/popcornにアクセスしてみます。
; charset=utf-8
期待される 🍿 popcorn ではなく 🥤 cream soad が返ってきました。
これはクロージャで変数をバインドすることで発生しています。
(今回であればクロージャtemp_func内でvalueを使用しています。/popcornにアクセスし、view_func(=temp_func)がvalueを返す時にバインドされている"cream soda"が返されてしまいます。)
簡単な例
より簡単な例は以下のようになります。
=
1, 2, 3の2乗が返されるように見えるため[1, 4, 9]を考えますが、実際には[9, 9, 9]が返ってきます。
Pythonのクロージャにおける遅延バインディングは "The Hitchhiker’s Guide to Python" などで詳しく確認できます7。
functools.partialを使用することで引数が評価され期待通りに動作します。
=
# [1.0, 4.0, 9.0]
関数のデフォルト引数が関数が定義されるタイミングで評価されることを利用することもできます8。
キャッシング機能を追加したりする際にも利用できますし、今回のようなケースで先に評価してもらうことで期待通り動作します。
=
# [1, 4, 9]
ただ、個人的にはfunctools.partialの方が意図を汲みやすい気がするので使っています。
endpoint
上記例ではadd_url_ruleをする際にendpoint=keyのようにエンドポイントを指定しています。
add_url_ruleにおいて、view_functionが登録される場所を簡略的に表すと以下のようになります。
(詳しくはflask/app.pyを確認してください。)
...
=
...
=
...
endpointが存在しない場合は.__name__を使用して関数の名前を設定するようにしています。
しかし、partialを使用した場合は.__name__を使用することができずエラーを吐きます。
そのため、endpoint=keyのように指定する必要があります。
:
同じendpointに対して複数の登録が確認されると、エラーが発生します。(flask/scaffold.pyの_endpoint_from_view_funcにて)
例えばpartialを使用せずview_functionにtemp_funcを使用し、endpointを指定しなかった場合などです。
: is :
最終的にResponseとしてview_functions内の関数がendpointをkeyとして返されます。
ルーティング
実際にappに登録されているルーティング等を確認したい場合は
- 直接
view_functionsを確認する url_mapでMap9を確認する
などがあります。
app.view_functions
lambda
app.url_map