On Mar 27, 2012, at 6:54 PM, Rich wrote:
> Looking for suggestions on a problem I've run into from time to time.
>
> I'm trying to use attribute events kindof like database triggers. The
> only problem is that I only get a "before" trigger
> on an attribute event where what I really need is an "after" trigger.
>
> For example: Given two tables, account and account_charges, I would
> like to keep a computed total of the charges on the account. Whenever
> a charge changes, I need to update the account. Using sqlalchemy
> events I can watch for the change and easily change the totals on the
> account based on the difference from the charges. However, if I want
> to do a full recalculation of the totals it becomes harder because if
> I do something like account.recalculate_charges() I get the total
> before the attribute is set.
>
> My current solution to this problem is to use before_flush events to
> calculate the totals whenever a charge changes. This has actually
> worked quite well for a long time, however as my application has
> gotten much, much more complex and I have many before_flush handlers
> to run, I gets very complicated to keep track of ordering and
> dependancies of my attribute changes.
>
> Does anybody else have better techniques for handling this situation?
This issue begins to move outside of the realm of the ORM, what I usually do
with such a thing is I invalidate the current calculated value when any of the
dependent attributes change; then I use a memoize decorator on top of the
actual calculated attribute itself. So basically lazy evaluation instead of
eval-on-change. If you use Pyramid I believe the "@reify" decorator does a
"memoize" kind of operation as does SQLAlchemy's @memoized_property.
The example below uses only Python itself. With SQLAlchemy you could use the
attribute events to handle the invalidation of the @memoized_property:
class memoized_property(object):
"""A read-only @property that is only evaluated once."""
def __init__(self, fget, doc=None):
self.fget = fget
self.__doc__ = doc or fget.__doc__
self.__name__ = fget.__name__
def __get__(self, obj, cls):
if obj is None:
return self
obj.__dict__[self.__name__] = result = self.fget(obj)
return result
class MyClass(object):
@property
def a(self):
return self.__dict__['a']
@a.setter
def a(self, value):
self.__dict__.pop('c', None)
self.__dict__['a'] = value
@property
def b(self):
return self.__dict__['b']
@b.setter
def b(self, value):
self.__dict__.pop('c', None)
self.__dict__['b'] = value
@memoized_property
def c(self):
print "evaluating a + b"
return self.a + self.b
m1 = MyClass()
m1.a = 2
m1.b = 4
print m1.c
print m1.c
m1.b = 8
print m1.c
--
You received this message because you are subscribed to the Google Groups
"sqlalchemy" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/sqlalchemy?hl=en.