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
というかなんでこんな処理いれたんだ??