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()
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.