What do you think about the pattern I've implemented for this purpose using metaclasses?
I've also pasted the code here but it's nicer to look at on github via the link above:
import logging
import sqlalchemy as sqla
import sqlalchemy.ext.declarative as decl
from .signals import signaler
log = logging.getLogger(u"cratejoy")
class _hooked(object):
def __init__(self, validate_func, normalize_func, field_name, private_name):
self.validate_func = validate_func
self.normalize_func = normalize_func
self.field_name = field_name
self.private_name = private_name
def __get__(self, instance, owner):
if not instance:
return getattr(owner, self.private_name)
val = getattr(instance, self.private_name)
return val
def __set__(self, instance, val):
namespace = instance.__class__.__name__ + "." + self.field_name
if self.normalize_func:
val = self.normalize_func(val)
if self.validate_func:
assert self.validate_func(val)
old_value = None
if hasattr(instance, self.private_name):
old_value = getattr(instance, self.private_name)
signaler.signal(namespace + ":before_update", instance=instance, new_value=val, old_value=old_value)
setattr(instance, self.private_name, val)
signaler.signal(namespace + ":after_update", instance=instance, new_value=val, old_value=old_value)
class DispatchingModelMeta(decl.DeclarativeMeta):
def __new__(cls, name, bases, attrs):
new_attrs = {}
for key, val in attrs.iteritems():
if isinstance(val, sqla.Column):
log.debug(u"{} Column {} {}".format(name, key, val))
val.key = key
validator_name = 'validate_' + key
normalize_name = 'normalize_' + key
private_name = '_' + key
validator_func = None
normalize_func = None
if validator_name in attrs:
validator_func = attrs[validator_name]
if normalize_name in attrs:
normalize_func = attrs[normalize_name]
new_attrs[private_name] = val
new_attrs[key] = _hooked(validate_func=validator_func, normalize_func=normalize_func, field_name=key, private_name=private_name)
else:
new_attrs[key] = val
return super(DispatchingModelMeta, cls).__new__(cls, name, bases, new_attrs)