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