What do you think about the pattern I've implemented for this purpose using metaclasses?
https://gist.github.com/aelaguiz/7691751 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)) if not val.name: val.name = key 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) On Wednesday, November 27, 2013 6:26:58 PM UTC-6, Michael Bayer wrote: > > > On Nov 27, 2013, at 12:48 PM, Amir Elaguizy <aela...@gmail.com<javascript:>> > wrote: > > > If I have a model like: > > > > class Test(Base): > > value = sqlalchemy.Column(db.String) > > > > and I have a function like: > > > > def on_value_change(model, oldValue): > > # Do stuff > > > > > > I'd like on_value_change called *after* Test.value has been changed > > yeah there’s been a bit of discussion about that but it isn’t present in a > simple way. attribute mechanics already take up a lot of overhead and add > lots of complexity so adding an “after” event isn’t something I’m in a > hurry to do. > > In the rare occasions that I need this, sometimes what I will do is, just > set the value within the before event, then work with it - I haven’t done > this much so YMMV: > > @event.listens_for(A.data, "set") > def set(target, value, oldvalue, initiator): > target.__dict__['data'] = value > work_with(target) > return value > > the reason __dict__ is used is otherwise you trigger an endless loop with > the event. The reason doing things in this way is dangerous (and why > it’s extra hard to make this work) is that if you pass around “target” to > other parts of your app, which are themselves doing things with attributes, > now you have a nesting pattern going on that can easily enter more endless > recursion types of issues. > > usually what I’ll do is just stick to simple things and use a descriptor > like a synonym or a hybrid to set the value, which does what it needs after > the set event. that’s pretty much the normal Python way of doing this sort > of thing in any case. Attribute events are in particular tailored > towards validating / processing the immediate value given, not so much > calling out into the bigger ecosystem of the application, as it is already > occurring within a critical part of the attribute mechanics. > > > -- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To unsubscribe from this group and stop receiving emails from it, send an email to sqlalchemy+unsubscr...@googlegroups.com. To post to this group, send email to sqlalchemy@googlegroups.com. Visit this group at http://groups.google.com/group/sqlalchemy. For more options, visit https://groups.google.com/groups/opt_out.