ActiveRecord Style Validation using SQLAlchemy and Elixir
Update: Turns out I missed the boat on this one. This has already been implemented in Elixir.
I just spent the better part of today putting together the pieces in order to create ActiveRecord like validation in my Elixir models. Here is what I wanted to be able to do:
class Tag(Entity):
label = Field(String(200))
before_insert('validate_label')
before_update('validate_label')
def validate_label(self):
Some validation code here
I wanted to be able to write custom validation methods in my model and then hook them into sqlalchemy events like ‘before_update’ so that before any data goes to my app’s database it is first validated. Then at that point I can catch it and redirect the user to change the invalid data.
Earlier I had stumbled upon this code by Beachcoder. While I was excited to see that it could work, I was having a difficult time understanding the approach, largely because a lot of this stuff was essentially a black box to me. So I got under the hood and came up with my own approach. I’m not sure if it’s better or even complete, (even though it seems to work and I pass all my unit tests), but that’s why I am posting it here. Feedback is welcome!
from sqlalchemy.orm import MapperExtension
from elixir.statements import Statement
class HookExtension(MapperExtension):
"""
Given a hookname and a list of callbacks, (which are just methods associated with the instance)
this mapper extension overrides the given hook with a function that executes each of the callbacks.
"""
def __init__(self, hookname, callbacks):
MapperExtension.__init__(self)
def execute_callbacks(mapper, connection, instance):
for f in callbacks: getattr(instance,f)()
setattr(self, hookname, execute_callbacks)
def create_hook_statement(hookname):
“”"
A simple class generator. Given a sqlalchemy hook name like ‘before_insert’ or ‘before_update’ creates
an Elixir statement. The statement excepts a list of ‘callbacks’ which are names of methods associated
with the Elixir class that will be executed based on the hook.
“”"
class HookStatement(object):
def __init__(self, entity, *callbacks):
ext = HookExtension(hookname, callbacks)
entity._descriptor.add_mapper_extension(ext)
return HookStatement
before_insert = Statement(create_hook_statement(’before_insert’))
before_update = Statement(create_hook_statement(’before_update’))
April 21st, 2008 at 4:14 am
As I said on Elixir’s mailing list, this is already implemented in Elixir’s core.
See
http://elixir.ematia.de/trac/browser/elixir/trunk/tests/test_events.py
for some examples.
November 12th, 2008 at 9:13 pm
ua3afxn0fg2tnanw