What's new? | Help | Directory | Sign in
Google
formalchemy
Auto-generated, customizable HTML input form fields from your SQLAlchemy mapped classes.
  
  
  
  
    
Search
for
Updated Mar 18, 2008 by conrad.alex
Labels: Phase-Implementation, Featured
FormAlchemyDocumentation02  
FormAlchemy 0.2 documentation.

Terminology

model: a SQLAlchemy mapped class.

Concepts

FormAlchemy was designed to ease the developer's work when dealing with SQLAlchemy mapped classes (models) in a web environement where HTML forms are often used. The basic concept is to generate HTML input fields from a given model that will match the model's columns definition. FormAlchemy will try to figure out what kind of HTML code should be returned by introspecting the model's properties and generate ready-to-use HTML code that will fit with the developer's application. Of course, FormAlchemy can't figure out everything, i.e, the developer might want to display only a few columns from the given model. Thus, FormAlchemy was design to be hightly customizable as well.

During development, FormAlchemy has derived from its initial goal and also features HTML table rendering from a single model or a collection.

FormAlchemy's current state

FormAlchemy is in alpha stage and the API is in constant evolution. So chances are that your code might break from a version to another, this until FormAlchemy 1.0 is released.

Usage

In the following examples, we will make the use of a arbitrary SQLAlchemy user model. The setup is defined as followed:

from sqlalchemy import *
from sqlalchemy.orm import *

meta = MetaData()

user_table = Table('users', meta,
    Column('id', Integer, primary_key=True),
    Column('email', Unicode(40), unique=True, nullable=False),
    Column('password', Unicode(20), nullable=False),
    Column('first_name', Unicode(20)),
    Column('last_name', Unicode(20)),
    Column('description', Unicode),
    Column('active', Boolean, default=True),
)

class User(object):
    pass

mapper(User, user_table)

user = User()

Imports

All FormAlchemy's objects live under the formalchemy package.

# Form related classes
from formalchemy import FieldSet, Field

# Table related classes
from formalchemy import Table, TableConcat, TableCollection

These two combined imports are equivalent to:

from formalchemy import *

Binding

Before you can render anything, you will have to bind your models to FormAlchemy's classes. All classes come with a bind() method:

fs = FieldSet()
fs.bind(user)

You can also bind the model as a bind keyword argument passed during class instantiation:

fs = FieldSet(bind=user)

or as an argument if passed in first position:

fs = FieldSet(user)

Rendering

All FormAlchemy classes come with a render() method. This is the method responsible for returning the appropriate generated HTML code.

html = fs.render()

html now holds a string representing the HTML code. Just print html to see the rendered code:

<fieldset>
  <legend>User</legend>
  <div>
    <label class="field_req" for="id">Id</label>
    <input id="id" name="id" type="text" />
  </div>
  <div>
    <label class="field_req" for="email">Email</label>
    <input id="email" maxlength="40" name="email" type="text" />
  </div>
  <div>
    <label class="field_req" for="password">Password</label>
    <input id="password" maxlength="20" name="password" type="text" />
  </div>
  <div>
    <label class="field_opt" for="first_name">First name</label>
    <input id="first_name" maxlength="20" name="first_name" type="text" />
  </div>
  <div>
    <label class="field_opt" for="last_name">Last name</label>
    <input id="last_name" maxlength="20" name="last_name" type="text" />
  </div>
  <div>
    <label class="field_opt" for="description">Description</label>
    <input id="description" name="description" type="text" />
  </div>
  <div>
    <label class="field_opt" for="active">Active</label>
    <input checked="checked" id="active" name="active" type="checkbox" value="True" />
  </div>
</fieldset>

You can also generate HTML by directly printing fs:

# Same as fs.render()
print fs

That's about it for rendering. There's nothing more you need to do to generate HTML output from your mapped classes.

FormAlchemy options

In the previous section, we learned to generate HTML code from a given model. But the user might want to have a different output. As FormAlchemy was designed with customization in mind, it is possible to alter the rendered HTML.

FormAlchemy has some default behaviour set: it is configured to render the most exhaustive HTML code possible to reflect from a bound model. Although, you can pass keyword arguments to FormAlchemy's classes to behave differently, thus altering the rendered HTML code. In FormAlchemy terms, these keyword arguments are usually designated as "options". Passing options can be done in many ways.

Options via the render() method

The most straightforward way is by passing keyword arguments to the render() method:

fs.render(pk=False, fk=False, exclude=["private_column"])

The options given at the render method are NOT persistant. You will need to pass these options everytime you call render to reproduce the same output or FormAlchemy will fallback to its default behaviour. Passing keyword options is usually meant to alter the HTML output on-the-fly.

Options at the SQLAlchemy class level

By creating a FormAlchemy subclass at the model level, i.e. the SQLAlchemy mapped class, it is possible to setup attributes which names and values correspond to the keyword arguments passed to render():

class User(object):
    class FormAlchemy:
        pk = False
        fk = False
        exclude = ["private_column"]

These attributes are persistant and will be used as FormAlchemy's default behaviour.

Options via the configure() method

All classes come with a configure() and a reconfigure() method. Passing the keyword arguments to these methods will alter the behaviour as well. These options are persistant and will be used as FormAlchemy's default behaviour.

fs.configure(pk=False, fk=False, exclude=["private_column"])

The configure() method updates the class' default behaviour. Any other previously set option will be kept intact. As opposed, the reconfigure() method will clear any previously set option before reconfiguration. If no option is passed, all set options will simply be removed.

Overriding options

In any case, persistant options set at the SQLAlchemy class level or via the configure() method will be overridden if those same options are passed to the render() method.

Alerting the output

Back to where we were. Let's alter the output by removing the model's "id" primary key column by passing a keyword argument:

print fs.render(pk=False)
<fieldset>
  <legend>User</legend>
  <div>
    <label class="field_req" for="email">Email</label>
    <input id="email" maxlength="40" name="email" type="text" />
  </div>
  <div>
    <label class="field_req" for="password">Password</label>
    <input id="password" maxlength="20" name="password" type="text" />
  </div>
  <div>
    <label class="field_opt" for="first_name">First name</label>
    <input id="first_name" maxlength="20" name="first_name" type="text" />
  </div>
  <div>
    <label class="field_opt" for="last_name">Last name</label>
    <input id="last_name" maxlength="20" name="last_name" type="text" />
  </div>
  <div>
    <label class="field_opt" for="description">Description</label>
    <input id="description" name="description" type="text" />
  </div>
  <div>
    <label class="field_opt" for="active">Active</label>
    <input checked="checked" id="active" name="active" type="checkbox" value="True" />
  </div>
</fieldset>

So the "id" column of our model has been excluded from rendering. By passing pk=False results in not rendering the "id" primary key. In fact, if our model had more than a single primary key, none of them would have been rendered.

So this is how you can alter the output of FormAlchemy. Multiple options can be passed to the render() method:

print fs.render(
    pk=False,
    exclude="active",
    password="password",
    legend="This a user form",
    cls_req="must_fill",
    cls_opt="can_fill"
)
<fieldset>
  <legend>This a user Form</legend>
  <div>
    <label class="must_fill" for="email">Email</label>
    <input id="email" maxlength="40" name="email" type="text" />
  </div>
  <div>
    <label class="must_fill" for="password">Password</label>
    <input id="password" maxlength="20" name="password" type="password" />
  </div>
  <div>
    <label class="can_fill" for="first_name">First name</label>
    <input id="first_name" maxlength="20" name="first_name" type="text" />
  </div>
  <div>
    <label class="can_fill" for="last_name">Last name</label>
    <input id="last_name" maxlength="20" name="last_name" type="text" />
  </div>
  <div>
    <label class="can_fill" for="description">Description</label>
    <input id="description" name="description" type="text" />
  </div>
</fieldset>

Notice that the rendered HTML is a little different from the previous example:

The options given to the render() method are not persistant. If you call render() again without passing any option, you will get unaltered HTML code.

Available FormAlchemy classes

The available form related classes are:

FormAlchemy has derived a little from its original goal and other, non-form related classes have been extended to the module:

All classes require to be bound to a model to generate HTML code. Although, some class might require extra configuration to produce code:

Available options

Here are the available keyword options that can be passed to FormAlchemy:

Column related options

Form related options

Here is an example of how the dropdown option can be used:
{"meal":
    {"opts":[("Hamburger", "HB"),
             ("Cheeseburger", "CB"),
             ("Bacon Burger", "BB"),
             ("Roquefort Burger", "RB"),
             ("Pasta Burger", "PB"),
             ("Veggie Burger", "VB")],
     "selected":["CB", "BB"],    ## Or just "CB"
     "multiple":True,
     "size":3,
    }
}

Table related options

Validation related options


Sign in to add a comment