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'))
No related posts.
Related posts brought to you by Yet Another Related Posts Plugin.
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.
April 26th, 2011 at 7:27 am
Nice post. I learn something much harder on different blogs everyday. Most commonly it is stimulating to learn to read content from other writers and practice something at their store. I’d love to use some with the content in my small blog whether you don’t mind. Natually I’ll provide link on your own web blog. Thank you sharing.