Thursday, October 16, 2008

FormAlchemy 1.0

A little background: a few months ago, I went looking for a web framework that was good at automating CRUD (create/retrieve/update/delete) against an existing database schema. I tried django but its database introspection abilities are beyond feeble, and django-sqlalchemy was not mature enough. I tried dbmechanic but its dozen-plus dependencies, most of which were alpha-quality, gave me pause; so did its basic architecture on top of toscawidgets, which I think is The Wrong Way to build web apps. (I understand that the former problem has since been reduced; the latter has not.)

So, I went back to option #3, FormAlchemy. I knew SQLAlchemy could reflect very hairy schemas indeed, and what it could not reflect, it could certainly represent with a little manual help. And FormAlchemy was a decent start to automating CRUD with SA models. I added the ability to represent relations, automatic syncing of form input back to SA objects, Grid support, and a test suite. Then Gael came along and added internationalization, support for even more SA features, and Sphinx docs. Along the way we've killed enough bugs and added enough test cases (yes, the two are related) that we think we have a pretty solid release. Especially since I just released 1.0.1 fixing the most obvious problems. :)

I think all three FA committers use it mostly with Pylons; that said, FormAlchemy has no dependencies besides SQLAlchemy itself. You could easily use it with werkzeug or web.py or whatever.

Here, finally, is a quick FormAlchemy tutorial:

To get started, you only need to know about two classes, FieldSet and Grid, and a handful of methods:

  • render: returns a string containing the html
  • validate: true if the form passes its validations; otherwise, false
  • sync: syncs the model instance that was bound to the input data

This introduction illustrates these three methods. For full details on customizing FieldSet behavior, see the documentation.

We'll start with two simple SQLAlchemy models with a one-to-many relationship (each User can have many Orders), and fetch an Order object to edit:

 from formalchemy.tests import Session, User, Order
session = Session()
order1 = session.query(Order).first()

Now, let's render a form to edit the order we've loaded.

 from formalchemy import FieldSet, Grid
fs = FieldSet(order1)
print fs.render()

This results in the following form elements:

Note how the options for the User input were automatically loaded from the database. str() is used on the User objects to get the option descriptions.

To edit a new object, bind your FieldSet to the class rather than a specific instance:

  fs = FieldSet(Order)

To edit multiple objects, bind them to a Grid instead:

 orders = session.query(Order).all()
g = Grid(Order, orders)
print g.render()

Which results in:

Saving changes is similarly easy. (Here we're using Pylons-style request.params(); adjust for your framework of choice as necessary):

 fs = FieldSet(order1, request.params())
if fs.validate():
    fs.sync()
    session.commit()

Grid works the same way. More details in the documentation; start with Form generation.

To give FormAlchemy a try, just easy_install it. If you have any questions, Alex and I are often in both #sqlalchemy and #pylons on freenode. And of course there's always the mailing list.

6 comments:

Anonymous said...

Great work and nice quick tutorial. I tried FA when it was version 0.2 or 0.3 and it has come along way, i'm going to try it out on my next project.
Keep going and well done on a good job getting to a stable release!
Nick

kumar said...

Hi Jonathan. That's pretty neat. Has anyone attempted to make an automatic admin interface (like Django's) with FormAlchemy? I've seen AdminPylon for SA and Pylons but I don't know of any others.

kumar said...

er, I should say, a "full blown admin interface" since I know that at a low level this is what you already got FormAlchemy to do.

Noah Gift said...

wow, this is excellent news Jonathan. I can't wait to try it this weekend.

Jonathan Ellis said...

Kumar: sort of -- I will post about my mini admin app soon, but if you are impatient, check out FA contrib/admin.py in svn and read the comments at the top. It doesn't do everything django's does (in particular, it leaves permissioning alone) but it does give you customizable CRUD out of the box and is quite a small amount of code, so it's easy to build on.

horli said...

You sayed you think ToscaWidgets are The Wrong Way to build web apps. Can you elaborate on that?
I am interested in your arguments since I have to make a decision to use Tosca or not.