.. _Pyramid: http://docs.pylonsproject.org/
.. _Colander: http://docs.pylonsproject.org/projects/colander/dev/
.. _Mail: mailto:aachurin@gmail.com

Installation Instructions
-------------------------

::

  pip install smallform

Basic Usage
-----------

The steps a developer must take to cause a form to be ready to accept are:
 - Define a schema
 - Create a form object
 - Validate a form

Defining A Schema
-----------------
The first step to using Smallform is to create a schema which represents the data structure you wish to capture.

See `Colander`_ for more information about schema definition.

::

  from colander import SchemaNode, MappingSchema
  from colander import String, Email, Length
  
  class MyScheme(MappingSchema):
      name = SchemaNode(String(), validator = Length(min=4))
      email = SchemaNode(String(), validator = Email())
      password = SchemaNode(String(), validator = Length(min=5))
      
  myscheme = MyScheme()

Or, we can create new form with the bound schema using *bind_schema* decorator:

::

  from colander import SchemaNode, MappingSchema
  from colander import String, Email, Length

  from smallform import bind_schema
  
  @bind_schema
  class MyForm(MappingSchema):
      name = SchemaNode(String(), validator = Length(min=4))
      email = SchemaNode(String(), validator = Email())
      password = SchemaNode(String(), validator = Length(min=5))

Create A From Object
--------------------
To create a form object, we do this:

::

  from smallform import Form
  
  myform = Form(myschema)

Or this, if we using bind_schema to create the form:

::

  myform = MyForm()

Additionly, we can set default form values:

::

  myform = MyForm(defaults=dict(name='default name'))

Validate A Form
---------------
Once we’ve created a Form object, we can validate it:

::

  values = myform.validate(request.POST)
  if not myform.errors:
      # processing values
      ...
  else:
      # processing errors

Furthermore, it’s possible to bind validated field values to an object instance, 
for example a SQLAlchemy model instance:

::

  obj = myform.bind(MyModel())

We can also use the parameters *include* and *exclude* to filter any unwanted data:

::
  
  obj = myform.bind(MyModel(), exclude=('password',))
  
Or

::

  obj = myform.bind(MyModel(), include=('name', 'email',))  

Working With Errors
-------------------
There are 4 methods available:
 - has_errors
 - get_errors
 - get_first_error
 - add_errors

Schema:

::

  from colander import SchemaNode, MappingSchema, TupleSchema
  from colander import String, Length

  from smallform import bind_schema
  
  class FooSchema(TupleSchema):
      a = SchemaNode(String())
      b = SchemaNode(String())

  class BarSchema(MappingSchema):
      a = SchemaNode(String())
      b = SchemaNode(String())
        
  @bind_schema
  class MyForm(MappingSchema):
      foo = FooSchema()
      bar = BarSchema()
      baz = SchemaNode(String(), validator = Length(min=5))

**Method has_errors(path)**

This method returns True if there are errors for the given path:

::

  if myform.has_errors('foo.0'): # string path form
    ...
  if myform.has_errors(('foo', 1)): # tuple/list path form
    ...
  if myform.has_errors('bar.a'): # string path form
    ...
  if myform.has_errors(['bar', 'a']): # tuple/list path form
    ...
  if myform.has_errors('baz'):
    ...

**Method get_errors(path)**

This method returns list of errors for the given path (None if there are no errors):

::

  errors = myform.get_errors('foo.0') # string path form
    ...
  errors = myform.get_errors(('foo', 1)) # tuple/list path form
    ...
  errors = myform.get_errors('bar.a') # string path form
    ...
  errors = myform.get_errors(['bar', 'a']) # tuple/list path form
    ...
  errors = myform.get_errors('baz')
    ...

**Method get_first_error(path)**

This method returns the first list item in the error list for the given path (None if there are no errors):

::

  error = myform.get_first_error('foo.0') # string path form
    ...
  error = myform.get_first_error(('foo', 1)) # tuple/list path form
    ...
  error = myform.get_first_error('bar.a') # string path form
    ...
  error = myform.get_first_error(['bar', 'a']) # tuple/list path form
    ...
  error = myform.get_first_error('baz')
    ...


**Method add_errors(path, errors or error)**

Sometimes it's necessary to make an additional validation:

::

  fields = myform.validate(request.POST)
  if fields:
    if fields['password'] != fields['confirm']:
        myform.add_errors('confirm', _('password and confirm don't match'))

  if myform.errors:
    ...

Using A Mako Template
---------------------
In your template:

::

  <form method="POST">
      <input name="name" type="text" value="${form.get('name')}" />
      % if form.has_errors('name'):
        <p>${form.get_first_error('name')}</p>
      % endif
      <input name="email" type="text" value="${form.get('email')}" />
      % if form.has_errors('email'):
        <p>${form.get_first_error('email')}</p>
      % endif
      <input name="password" type="password" value="" />
      % if form.has_errors('password'):
        <p>${form.get_first_error('password')}</p>
      % endif
  </form>

Feedback
--------
If you found errors or have ideas send me `Mail`_ 

