What is form library?
- anyone says, 'it is validation library for post/get data'
- another says, 'it is rendering library for form element'
marshmallow-form is not above one. form library is 'a container for presentation metadata'. so, form object is just a container.
- 'rendering form element', it is a task of template library(mako, jinja2, ...).
- 'validation post/get data', it is a task of schema library(colander, marshmallow, ...).
marshmallow-form is just a metadata container.
- having metadata anywhere
- nested field support
- accessing schema
- building your own form library
from pypi package repository.
# pip install marshmallow-form
from repository
git clone [email protected]/podhmo/marshmallow-form.git cd marshmallow-form python setup.py develop
the way of form definition.
import marshmallow_form as mf
class PersonForm(mf.Form):
name = mf.String(label="名前", placeholder="foo", widget="str")
age = mf.Integer(label="年齢", placeholder="0", widget="int")
class ParentsForm(mf.Form):
father = mf.Nested(PersonForm, label="父親")
mother = mf.Nested(PersonForm, label="母親")
- define form class with marshmallow_form.Form
- using field classes, define form fields.
- label and placeholder is metadata for presentation
with template library, form is just a metadata container. so, a above definition, using form as metadata container.
print(form.father.name["label"]) # => '名前' # 'name' in japanese
print(form.father.name["placeholder"]) # => 'foo'
print(form.name.value) # => 'foo'
- accessing metadata with __getitem__.
- accessing initial data or passed data with .value
with template(mako). deciding rendering method, using widget metadata.
from mako.template import Template
template = Template("""
<%def name="field(f)">\
${getattr(self, "field_" + f["widget"])(f)}
</%def>
<%def name="field_str(f)">\
<input type="text" name="${f.name}" value="${f.value}" placeholder="${f["placeholder"]}"/>
</%def>
<%def name="field_int(f)">\
<input type="number" name="${f.name}" value="${f.value}" placeholder="${f["placeholder"]}"/>
</%def>
<form action="#" method="POST">
%for f in form:
${field(f)}
%endfor
</form>
""")
print(template.render(form=form))
output.
<form action="#" method="POST">
<input type="text" name="father.name" value="" placeholder="foo"/>
<input type="number" name="father.age" value="0" placeholder="0"/>
<input type="text" name="mother.name" value="" placeholder="foo"/>
<input type="number" name="mother.age" value="0" placeholder="0"/>
</form>
from marshmallow.validate import Length
from marshmallow import ValidationError
class MLength(Length):
message_min = 'Too short! {min}.'
message_max = 'Too long! {max}.'
class AuthenticationForm(mf.Form):
name = mf.String()
password = mf.String(validate=MLength(5))
password_confirm = mf.String()
@mf.Form.validator
def same(schema, data):
if data["password"] != data["password_confirm"]:
raise ValidationError("not same!", "password")
input_data = {"name": "foo", "password": "*", "password_confirm": "+"}
form = AuthenticationForm(input_data)
print(form.validate()) # False
print(form.errors) # {'password': ['Too short! 5.', 'not same!']}
{'password': ['Too short! 5.', 'not same!']}
- form metadata
- field metadata
- metadata inheritance
- metadata override
form metadata
import marshmallow_form as mf
class MyForm(mf.Form):
name = mf.Str()
class Meta:
metadata = {"action": "#"}
form = MyForm()
form["action"] # => #
form.metadata["method"] = "post"
form["method"] # => "post"
MyForm()["method"] # => ""
field metadata
class MyForm2(mf.Form):
name = mf.Str()
ctime = mf.DateTime(disable=True)
form = MyForm2()
form.ctime["disable"] # => True
metadata inheritance
from functools import partial
DateTime = partial(mf.DateTime, widget="tdcalendar")
class MyForm3(mf.Form):
ctime = DateTime()
utime = DateTime()
form = MyForm3()
form.ctime["widget"] # => "tdcalendar"
form.utime["widget"] # => "tdcalendar"
metadata override
class MyForm4(MyForm3):
class Meta:
overrides = {"ctime": {"widget": "mycalendar"}}
form = MyForm4()
form.ctime["widget"] # => "mycalendar"
form.utime["widget"] # => "tdcalendar"
or with nested
class PersonForm(mf.Form):
name = mf.String(label="名前", placeholder="foo", widget="str")
age = mf.Integer(label="年齢", placeholder="0", widget="int")
class ParentsForm(mf.Form):
father = mf.Nested(PersonForm, label="父親", overrides={"name": {"label": "父親の名前"}})
mother = mf.Nested(PersonForm, label="母親")
form = ParentsForm()
form.father["label"] # => "父親"
form.father.name["label"] # => "父親の名前"
form.mother.name["label"] # => "名前"
- modify field
- add field
- remove field
modify field
form = StudentForm()
form.color.metadata["pairs"] = [("red", "red"), ("blue", "blue")]
form.color["pairs"] # => [('red', 'red'), ('blue', 'blue')]
add field
class StudentForm(mf.Form):
color = mf.Select([])
name = mf.Str()
form = StudentForm(initial={"grade": 3})
form.add_field("grade", mf.Int(label="学年"))
form.grade.value # => 3
form.grade["label"] # => "学年"
[f.name for f in form] # => ['color', 'name', 'grade']
remove field
form = StudentForm()
form.remove_field("color")
[f.name for f in form] # => ['name']
- schema class
- schema instance
schema class
from collections import namedtuple
Person = namedtuple("Person", "name age")
class PersonForm(mf.Form):
name = mf.Str()
age = mf.Int()
def make_object(self, data):
return Person(**data)
PersonForm.Schema # => <class 'marshmallow.schema.PersonSchema'>
schema = PersonForm.Schema(many=True)
schema.dump([Person("foo", 20), Person("bar", 20)]).data
# => OrderedDict([('name', 'foo'), ('age', 20)]), OrderedDict([('name', 'bar'), ('age', 20)])
schema instance
form = PersonForm()
form.schema.load({"name": "foo", "age": 20}).data # => Person(name='foo', age=20)
- define your form field class
- define the way of rendering
define your form field class
if just only adding default metadata, using functools.partial.
import functools
PositiveInt = functools.partial(mf.Int, validate=lambda x: x > 0)
class Form(mf.Form):
x = PositiveInt()
print(Form({"x": "-10"}).load())
# UnmarshalResult(data=OrderedDict([('x', None)]), errors={'x': ['Validator <lambda>(-10) is False']})
if define your own field class
from marshmallow.fields import Field
from marshmallow.exceptions import UnmarshallingError
import base64
class Base64(Field):
""" tiny base64 field"""
def __init__(self, *args, **kwargs):
super(Base64, self).__init__(*args, **kwargs)
def _serialize(self, value, attr, obj):
return base64.encodebytes(value)
def _deserialize(self, value):
try:
return base64.decodebytes(value.encode("utf-8"))
except:
raise UnmarshallingError("oops")
MyBase64 = mf.field_factory(Base64)
class Form(mf.Form):
x = MyBase64(label="this is broken")
form = Form({"x": "MTEx"})
print(form.load())
# UnmarshalResult(data=OrderedDict([('x', b'111')]), errors={})
define the way of rendering
def input(field, placeholder=""):
fmt = '<input name="{name}" value="{value}" placeholder="{placeholder}">'
return fmt.format(name=field["name"], value=field.value, placeholder=placeholder)
class Form(mf.Form):
name = mf.Str(__call__=input)
form = Form()
print(form.name(placeholder="foo"))
# => <input name="" value="" placeholder="foo">