Flask-WTF0.9 で仕様変更
Flask-WTF0.8 を使っていて、0.9 にアップデートしたら色々互換性が無くなっていてハマった。
Field モジュールの廃止
0.8.x で動いてたコード
from datetime import timedelta from flask.ext.wtf import Form, TextField, validators from flask.ext.babel import gettext as _ class Picture(Form): TIME_LIMIT = timedelta(minutes=0) title = TextField( _('title'), validators=[ validators.Required(message=_(u'Title field is required.')), validators.Length(min=1, max=50) ] ) description = TextField( _('description'), validators=[ validators.Required(message=_(u'Description field is required.')), validators.Length(min=1, max=254) ] )
TextField, validators がなくなった。
次のようにする。
-from flask.ext.wtf import Form, TextField, validators +from flask.ext.wtf import Form +from wtforms import TextField, validators
CSRF トークンの仕様変更
これがめっちゃハマった。
csrf のトークンが HTML にちゃんと生成されているのに、{'csrf_token': [u'CSRF token missing']} というエラーになる。
原因は
{{ form.csrf_token }}
で、生成した CSRF トークンが "##b62ca3f96cb9c47efb0954e197dcf76e8bf469d6" というような値だが、flask_wtf/csrf.py の以下の部分でエラーになってた。
def validate_csrf(data, secret_key=None, time_limit=None): """Check if the given data is a valid csrf token. :param data: The csrf token value to be checked. :param secret_key: A secret key for mixing in the token, default is Flask.secret_key. :param time_limit: Check if the csrf token is expired. default is True. """ if not data or '##' not in data: return False expires, hmac_csrf = data.split('##', 1) try: expires = float(expires) # ここで例外 except: return False
CSRF トークンの前にタイムスタンプが付与されるのが正しいが、付与されてないためエラーになった。
{{ form.csrf }}
ではなく、
<input type="hidden" id="csrf_token" value="{{ csrf_token() }}" name="csrf_token" />
というように Jinja2 の関数として呼び出してやらないといけない。
Jinja2 の関数として登録するには CsrfProtection() クラスのインスタンスを生成し、Flask のインスタンスを渡してやらないと行けない。
思いっきりドキュメントに書いてあったけど、わざわざソース読んで時間無駄にした orz
CSRF Protection — Flask-WTF 0.15
とか思ったけど本当の原因はこれだった…。
from datetime import timedelta from flask.ext.wtf import Form, TextField, validators from flask.ext.babel import gettext as _ class Picture(Form): TIME_LIMIT = timedelta(minutes=0) # これ
これで時間が 00:00:00 になってしまい、ただしく CSRF トークンが生成されてなかった orz
なので、今まで通り
{{ form.csrf_token }} としても正しく動いた orz
というかなんでこんな処理いれたんだ??