marshmallow-formに継承の対応とlayoutシステム付けました

marshmallow-formに継承の対応とlayoutシステム付けました。

marshmallow-form に以下のような機能を付けました。

  • 継承の対応
  • ネストしたフォームを順序付きで利用
  • Layoutシステム

今回一番の変更はLayoutシステムな気がします。

継承の対応

むしろ無かったことに気づきませんでした。以下の様なことが出来るようになったということです。

import marshmallow_form as mf


class PersonForm(mf.Form):
    name = mf.String()
    age = mf.Int()


class StudentForm(PersonForm):
    grade = mf.String()


form = StudentForm(initial={"name": "foo", "age": 10, "grade": 4})
for f in form:
    print(f.name, f.value)
# name foo
# age 10
# grade 4

普通ですね。

ネストしたフォームを順序付きで利用

コレも対応していなかったみたいです。

class FooForm(mf.Form):
    x0 = mf.Int()
    x1 = mf.Int()
    x2 = mf.Int()


class BarForm(mf.Form):
    y0 = mf.Nested(FooForm)
    y1 = mf.Nested(FooForm)


form = BarForm()
for f in form:
    print(f.name)
# y0.x0
# y0.x1
# y0.x2
# y1.x0
# y1.x1
# y1.x2

layoutシステム?

layoutシステム的なものが欲しかったので実装しました。なぜ欲しかったと言うと以下の様なケースです。 「日付のデータを入力しようとする時に、(year,month,day)のような3つのフィールドを1つの行として扱いたい。 一方で、他の行は通常通り1行に1フィールド表示したい」というようなケースです。 もちろん直接手書きをしてしまえば、どの行にどのように配置するなどを自由に記述できるのですが、 genericなrender関数のようなものに渡したい時にそのようにはいきません。

例えば以下のような形でrenderingしたいのです。

[(name)]
[(year, month, day)]

もちろんこのようなレイアウトでレンダリングしたいと言ってもどのようにレンダリングするのか分からなければrender関数も書けないので、 実際には以下のようなけいしきでiterateします。

[(lcolumn, (name,))]
[(lcolumn, (year, month, day))]

lcolumnはメタデータを保持した辞書のようなオブジェクトです。layoutシステムの定義時にも使います。

import marshmallow_form as mf


class DateTriple(mf.Form):
    year = mf.Int()
    month = mf.Int()
    day = mf.Int()


class PersonForm(mf.Form):
    name = mf.String()
    age = mf.String()
    birth = mf.Nested(DateTriple)

    class Meta:
        layout = mf.Layout([
            mf.LColumn("name", widget="default"),
            mf.LColumn("age", widget="default"),
            mf.LColumn("birth.year", "birth.month", "birth.day", widget="date"),
        ])


def render(widget, fields):
    if widget == "default":
        for f in fields:
            print("{}: {}".format(f.name, f.value))
    elif widget == "date":
        from datetime import date
        print(date(fields[0].value, fields[1].value, fields[2].value))

data = {"name": "foo", "age": 10, "birth": {"year": 2000, "month": 11, "day": 1}}
form = PersonForm(initial=data)

for lcol, fields in form:
    render(lcol["widget"], fields)
# name: foo
# age: 10
# 2000-11-01