class Person(SQLObject):
first_name = StringCol()
last_name = StringCol()
Then I made a person controller that looked like this:
class PersonController(CRUDController):
model = model.Person
individual = 'person'
template_prefix = '/person'
conditions = dict(orderBy='last_name')
I'm using class attributes to tell the CRUDController where the
templates are, what an individual instance should be called, and
conditions are passed into the SQLObject query that displays the
list. As I mentioned, it does use pagination which is kind of cool,
here's the CRUDController I put in lib/base.py:
class CRUDController(BaseController):
def index(self):
h.redirect_to(action='list')
def new(self):
setattr(c, self.__class__.individual, {})
m.subexec(self.__class__.template_prefix + '/new.myt')
def create(self):
obj = m.request_args[self.__class__.individual]
self.__class__.model(**obj)
h.redirect_to(action='list')
def destroy(self, id):
self.__class__.model.get(id).destroySelf()
h.redirect_to(action='list')
def edit(self, id):
obj = self.__class__.model.get(id)
objdict = dict([(x, getattr(obj, x)) for x in
obj._columnDict.keys()])
objdict['id'] = obj.id
setattr(c, self.__class__.individual, objdict)
m.subexec(self.__class__.template_prefix + '/edit.myt')
def update(self, id):
obj = self.__class__.model.get(id)
objargs = m.request_args[self.__class__.individual]
obj.set(**objargs)
h.redirect_to(action='detail')
def list(self):
options = getattr(self.__class__, 'conditions', {})
pages, collection = paginate(self.__class__.model,
m.request_args.get('page', 1), **options)
setattr(c, self.__class__.individual + '_pages', pages)
setattr(c, self.__class__.individual + '_collection',
collection)
m.subexec(self.__class__.template_prefix + '/list.myt')
def detail(self, id):
setattr(c, self.__class__.individual,
self.__class__.model.get(id))
m.subexec(self.__class__.template_prefix + '/detail.myt')
There's just 5 fairly short templates used to make this come
together. I've attached them as a zip to this message if you're
curious what they look like.
Cheers,
Ben
True, there's so few ORM's out there, I was rather hoping just to
keep the wrappers necessary inside the paginate package. I will add a
fall-back so that if the ORM class used is not SQLObject or
SQLAlchemy, it will assume that it can support the two container
operations needed (len and slicing).
- Ben
> True, there's so few ORM's out there, I was rather hoping just to
> keep the wrappers necessary inside the paginate package. I will add a
> fall-back so that if the ORM class used is not SQLObject or
> SQLAlchemy, it will assume that it can support the two container
> operations needed (len and slicing).
I've updated WebHelpers so that the Pagination ORM support can now
handle being tossed an SQLAlchemy Mapper or Table object. I've also
been playing around with generic controller stuff a bit with
SQLAlchemy, trying to work out best use patterns with Pylons and
organization of the resources.
The approach Django makes by having a way to setup Form information,
validation information, and db information, all within a fairly
concise Model metaclass looks rather appealing. I've been toying with
several approaches that are similar, however there's no use repeating
their work if there's not a massive payoff. The payoff I have in mind
here, is something that is framework-neutral, and uses a
significantly more powerful ORM, SQLAlchemy. Huy Do has been working
on some stuff that uses a combination in a similar spirit, but keeps
the added flexibility of SA so I'm looking into that as well.
But back to the generics and SQLAlchemy.....
The layout looks like this:
models/
__init__.py - Stores the actual plain old Python classes
that are used
tables.py - Stores the SQLAlchemy table definitions
forms.py - Stores the FormEncode schemas
contentstor.py - Some helpers for making a more functional Model
class
I then setup the Python class to have a validate method that uses the
FormEncode schema. One of the nice things about SQLAlchemy is that
you can delay and even swap the db engine at a later point. This made
it rather easy to put a database option in the config file, yet be
able to load it all up from the command prompt and connect the models
to the db of my choosing.
This time when making the generics, rather than having you sub-class
them, you continue to sub-class your BaseController, and we use
multiple inheritance to mix-in the methods desired. There's two
classes, one with list/detail views, one with edit/destroy/create
views. It's even fairly easy for the list view to know if the edit/
destroy/create are present, so you can show Edit/Destroy/New options
if they're mixed in.
Here's the user.py controller:
from app1.lib.base import *
class UserController(BaseController, generics.View, generics.Modify):
model = model.User
conditions = dict(order_by='name')
Of course, we still need to make the templates, but you'll want to
customize those no matter what. I'm including in this tarball, my
models dir, the templates relevant, and the lib files relevant for
those interested. It's a rather small set of code, which makes it a
heck of a lot quicker for getting CRUD going with SQLAlchemy, and I
see the process only improving in the future.
I should note that I kept the FormEncode schema separate, because its
very useful in many forms to combine schemas for larger, more complex
forms. Maybe you're editing 4 users at once, or editing a user plus
related content. FormEncode makes it a snap to include schemas back
and forth so you can validate the whole request params in one go
before having to even bother pushing the data into individual objects
and validate it there.
Please note that I called my app 'app1', you'll need to change this
where appropriate before using it. Also remember to set a db connect
string in the dev ini file under your [app:main] section called
database.
Cheers,
Ben
Thanks!