This is a pre-production deployment of Warehouse, however changes made here WILL affect the production instance of PyPI.
Latest Version Dependencies status unknown Test status unknown Test coverage unknown
Project Description

motivation

form library is not validation library

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.

features

  • having metadata anywhere
  • nested field support
  • accessing schema
  • building your own form library

getting started

install

from pypi package repository.

# pip install marshmallow-form

from repository

git clone git@github.com/podhmo/marshmallow-form.git
cd marshmallow-form
python setup.py develop

form class definition

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

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>

validation

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!']}

detail

having metadata anywhere

  • 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"]  # => "名前"

dynamic form

  • 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']

accessing schema

  • 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)

building your own form library

  • 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">
Release History

Release History

0.1.1

This version

History Node

TODO: Figure out how to actually get changelog content.

Changelog content for this version goes here.

Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit.

Show More

0.1

History Node

TODO: Figure out how to actually get changelog content.

Changelog content for this version goes here.

Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit.

Show More

Download Files

Download Files

TODO: Brief introduction on what you do with files - including link to relevant help section.

File Name & Checksum SHA256 Checksum Help Version File Type Upload Date
marshmallow-form-0.1.1.tar.gz (15.0 kB) Copy SHA256 Checksum SHA256 Source May 3, 2015

Supported By

WebFaction WebFaction Technical Writing Elastic Elastic Search Pingdom Pingdom Monitoring Dyn Dyn DNS HPE HPE Development Sentry Sentry Error Logging CloudAMQP CloudAMQP RabbitMQ Heroku Heroku PaaS Kabu Creative Kabu Creative UX & Design Fastly Fastly CDN DigiCert DigiCert EV Certificate Rackspace Rackspace Cloud Servers DreamHost DreamHost Log Hosting