Author: luke
Date: 2007-04-18 11:49:00 -0600 (Wed, 18 Apr 2007)
New Revision: 2565
Modified:
SQLObject/branches/cache-deps-on-views/sqlobject/materialized/dependency.py
SQLObject/branches/cache-deps-on-views/sqlobject/materialized/materialized.py
SQLObject/branches/cache-deps-on-views/sqlobject/materialized/tests/test_materialized.py
Log:
cacheObject updates on create/set/delete
Modified: SQLObject/branches/cache-deps-on-views/sqlobject/materialized/dependency.py
===================================================================
--- SQLObject/branches/cache-deps-on-views/sqlobject/materialized/dependency.py 2007-04-18 17:47:46 UTC (rev 2564)
+++ SQLObject/branches/cache-deps-on-views/sqlobject/materialized/dependency.py 2007-04-18 17:49:00 UTC (rev 2565)
@@ -3,6 +3,7 @@
from sqlobject.events import *
from sqlobject.include.pydispatch import dispatcher
from sqlobject import classregistry
+from sqlobject import SQLObject
from operator import isCallable
import inspect
@@ -104,7 +105,6 @@
def __init__(self, target, source, *extras):
if isinstance(source[0], sqlbuilder.SQLExpression):
source = (str(source[0].tableName), str(source[0].fieldName)) + source[1:]
- print source
if len(source) == 3:
other = source[2]
try:
@@ -115,6 +115,8 @@
source = source[:2]
else:
extras = (None,) + extras
+ if target[1][:5] == '_get_':
+ target = (target[0], target[1][5:])
self.target = self.DependencyItemClass(target, *extras)
self.source = self.DependencyItemClass(source, *extras)
@@ -130,44 +132,41 @@
targetClass = classregistry.findClass(targetClassName, class_registry=inst.sqlmeta.registry)
- print "Route", route
if inspect.isfunction(route):
argspec = inspect.getargspec(route)
- print "argspec", argspec
if len(argspec[0]) == len(argspec[3]) and not argspec[1] and not argspec[2]:
- print "exec", route()
route = route()
- print "RouteX", route
if route is None:
route = lambda i,c: [i]
elif isinstance(route, sqlbuilder.SQLExpression):
- route = lambda i,c,r=route: c.select(sqlbuilder.AND(r, i))
+ route = lambda i,c,r=route: c.select(sqlbuilder.AND(r, i), connection=i._connection)
ret.setdefault((targetClassName, targetAttr),[]).extend(route(inst, targetClass))
return ret
- def process(self, inst, attrs):
- for (cls, attr), insts in self.instancesToProcess(inst, attrs).iteritems():
+ def process(self, inst, attrs, _toProcess=None):
+ if _toProcess is None:
+ _toProcess = self.instancesToProcess(inst, attrs)
+ for (cls, attr), insts in _toProcess.iteritems():
+ print insts
for inst in insts:
- inst._SO_resetCache(attr)
+ inst._SO_updateCacheObject([attr])
+ self.process(inst, [attr])
-
- def FK(self, name):
- '''route for foreign key'''
- def f(inst, cls):
- return cls.select(cls.q.id==getattr(inst, name+'ID'))
- return f
-
- def J(self, name):
- '''route for join'''
- def f(inst, cls):
- return cls.select(getattr(cls.q,name)==inst.id)
- return f
-
- def S(self, *args):
- '''route for self'''
- if not len(args):
- return self.S
- return args[0]
dep = SQLDependencyManager()
+
+
+def _processDepsOnChange(inst, kwargs):
+ dep.process(inst, kwargs.keys())
+
+listen(_processDepsOnChange, SQLObject, RowUpdatedSignal)
+listen(_processDepsOnChange, SQLObject, RowCreatedSignal)
+
+def _processDepsOnDelete(inst, post_funcs):
+ attrs = ['id'] + inst.sqlmeta.columns.keys()
+ toProcess = dep.instancesToProcess(inst, attrs)
+ def f(inst, toProcess=toProcess):
+ dep.process(inst, attrs, _toProcess=toProcess)
+ post_funcs.append(f)
+listen(_processDepsOnDelete, SQLObject, RowDestroySignal)
\ No newline at end of file
Modified: SQLObject/branches/cache-deps-on-views/sqlobject/materialized/materialized.py
===================================================================
--- SQLObject/branches/cache-deps-on-views/sqlobject/materialized/materialized.py 2007-04-18 17:47:46 UTC (rev 2564)
+++ SQLObject/branches/cache-deps-on-views/sqlobject/materialized/materialized.py 2007-04-18 17:49:00 UTC (rev 2565)
@@ -1,22 +1,51 @@
-from sqlobject import SQLObject, sqlmeta as _sqlmeta
-from sqlobject import events
+from sqlobject import SQLObject, SQLObjectNotFound, sqlmeta as _sqlmeta
+from sqlobject import events, col
+from sqlobject.main import makeProperties
from dependency import *
+class materialized_sqlmeta(_sqlmeta):
+ cachedIn = None
+
+ def setClass(cls, soClass):
+ super(materialized_sqlmeta, cls).setClass(soClass)
+
+ cls.cachedAttributes = {}
+
+ cachedIn = cls.cachedIn
+ if cachedIn is None:
+ cachedIn = cls.table + '_cache'
+ class cache_sqlmeta:
+ table = cachedIn
+
+ cls.cacheClass = type(cls.soClass.__name__+'Cache', (SQLObject,), {'sqlmeta': cache_sqlmeta})
+
+ setClass = classmethod(setClass)
+
class MaterializedSQLObject(SQLObject):
- class sqlmeta(_sqlmeta):
- cachedIn = None
+ sqlmeta = materialized_sqlmeta
- def setClass(cls, soClass):
- super(cls, sqlmeta).setClass(soClass)
-
- if cls.cachedIn is None:
- cls.cachedIn = cls.table + '_cache'
- cls.cacheClass = type(cls.soClass.__name__+'Cache', (SQLObject,))
-
- setClass = classmethod(setClass)
+ def _init(self, id, connection=None, **kw):
+ super(MaterializedSQLObject, self)._init(id, connection=connection, **kw)
+ try:
+ self._SO_cacheObject = self.sqlmeta.cacheClass.get(self.id, connection=connection)
+ except SQLObjectNotFound:
+ self._SO_cacheObject = None
+
+ def _create(self, id, **kw):
+ super(MaterializedSQLObject, self)._create(id, **kw)
+ self._SO_cacheObject = self.sqlmeta.cacheClass(id=self.id)
+ def _SO_updateCacheObject(self, attrs):
+ for attr in attrs:
+ cacheAttr = self.sqlmeta.cachedAttributes.get(attr, None)
+ if cacheAttr:
+ new_value = cacheAttr.func(self)
+ self._SO_cacheObject.set(**{attr+'_dirty': False,
+ attr: new_value})
+
def _addColumn(cls, connection, column_name, column_definition, changeSchema, post_funcs):
+ ''' for automatically adding dependencies to foreign keys'''
def f(cls, col):
if col.foreignKey:
dep.add((cls.__name__, col.name), (col.foreignKey, 'id', lambda cls=cls,col=col: getattr(cls.q, col.origName)))
@@ -25,6 +54,7 @@
events.listen(_addColumn, MaterializedSQLObject, events.AddColumnSignal)
def _addJoin(cls, join_name, join_definition, post_funcs):
+ ''' for automatically adding dependencies to joins'''
def f(cls, join):
dep.add((cls.__name__, join.joinMethodName), (join.otherClassName, 'id', lambda cls=cls,join=join: getattr(cls.q, join.joinMethodName)))
post_funcs.append(f)
@@ -36,18 +66,21 @@
def __init__(self, func, colType, name=None):
self.func = func
self.colType = colType
+
if name is None:
- self.name = self.func.__name__
- else:
- self.name = name
+ name = self.func.__name__
+ if name[:5] == '_get_':
+ name = name[5:]
+ self.name = name
self.__name__ = self.name
self.__dict__.update(func.__dict__)
def __call__(self, obj):
val = None
- if getattr(obj, '_SO_cacheInstance', None):
- val = getattr(obj._SO_cacheInstance, self.name)
+ if getattr(obj, '_SO_cacheObject', None):
+ if not getattr(obj._SO_cacheObject, self.name+"_dirty"):
+ val = getattr(obj._SO_cacheObject, self.name)
if val is None:
val = self.func(obj)
return val
@@ -58,12 +91,13 @@
return decorate
-
-def _processCacheClass(new_class_name, bases, new_attrs, post_funcs, early_funcs):
+def _addColumnsToCacheClass(new_class_name, bases, new_attrs, post_funcs, early_funcs):
def f(cls):
for name, value in new_attrs.items():
if isinstance(value, MaterializedAttr):
- cls.sqlmeta.
- post_funcs.append(f)
+ cls.sqlmeta.cachedAttributes[value.name] = value
+ cls.sqlmeta.cacheClass.sqlmeta.addColumn(value.colType(name=value.name, default=None))
+ cls.sqlmeta.cacheClass.sqlmeta.addColumn(col.BoolCol(name=value.name+'_dirty', default=True))
+ post_funcs.extend((f,makeProperties))
-listen(_processCacheClass, MaterializedSQLObject, ClassCreateSignal)
\ No newline at end of file
+listen(_addColumnsToCacheClass, MaterializedSQLObject, ClassCreateSignal)
Modified: SQLObject/branches/cache-deps-on-views/sqlobject/materialized/tests/test_materialized.py
===================================================================
--- SQLObject/branches/cache-deps-on-views/sqlobject/materialized/tests/test_materialized.py 2007-04-18 17:47:46 UTC (rev 2564)
+++ SQLObject/branches/cache-deps-on-views/sqlobject/materialized/tests/test_materialized.py 2007-04-18 17:49:00 UTC (rev 2565)
@@ -10,11 +10,12 @@
class MaterializedOne(MaterializedSQLObject):
name = StringCol()
- twos = SQLMultipleJoin('MaterializedTwo', joinColumn='one_id')
+ twos = SQLMultipleJoin('MaterializedTwo', joinColumn='oneID')
@cachedAs(IntCol)
- @dep.dependentOn('MaterializedOne', 'twos', MO.q.twos)
+ @dep.dependentOn('MaterializedOne', 'twos')
def _get_twoCount(self):
+ print "getting twoCount", self.twos.count()
return self.twos.count()
@cachedAs(IntCol)
@@ -41,8 +42,15 @@
def _get_name2(self):
return self.one.name + self.detail
+ @cachedAs(StringCol)
+ @dep.dependentOn('MaterializedTwo', 'detail')
+ @dep.dependentOn('MaterializedTwo', 'name')
+ def _get_name3(self):
+ ''' dep on dep '''
+ return self.name + self.detail
+
def setup_module(mod):
- setupClass([MaterializedOne, MaterializedTwo])
+ setupClass([MaterializedOne, MaterializedTwo, MaterializedOne.sqlmeta.cacheClass, MaterializedTwo.sqlmeta.cacheClass])
mod.ones = inserts(MaterializedOne, (('S',),
('T',),
), 'name')
@@ -54,8 +62,8 @@
def testSetup():
deps = dep.get('MaterializedOne', 'name')
- assert set([(x,y) for x,y,z in deps]) == set([('MaterializedTwo','_get_name'),
- ('MaterializedTwo','_get_name2')])
+ assert set([(x,y) for x,y,z in deps]) == set([('MaterializedTwo','name'),
+ ('MaterializedTwo','name2')])
assert [z for x,y,z in deps][0] != [None, None]
def testSetupDepsForFK():
@@ -73,14 +81,62 @@
def testProcessInstances():
assert dep.instancesToProcess(ones[0], ['name']) == {
- ('MaterializedTwo', '_get_name'): [twos[0], twos[1]],
- ('MaterializedTwo', '_get_name2'): [twos[0], twos[1]],
+ ('MaterializedTwo', 'name'): [twos[0], twos[1]],
+ ('MaterializedTwo', 'name2'): [twos[0], twos[1]],
}
-def testCacheObject():
+def testCacheClass():
assert MaterializedOne.sqlmeta.cacheClass.sqlmeta.table == 'materialized_one_cache'
# Overridden by MaterializedTow.sqlmeta.cachedIn
assert MaterializedTwo.sqlmeta.cacheClass.sqlmeta.table == 'cache_materialized_two'
+
+ assert 'name' in MaterializedTwo.sqlmeta.cacheClass.sqlmeta.columns
-def testCacheObject():
- assert ones[0]._SO_cacheObject is None
\ No newline at end of file
+def testManualCacheObject():
+ obj = twos[0]
+ assert hasattr(obj, '_SO_cacheObject')
+ assert obj._SO_cacheObject.name == None
+ obj._SO_cacheObject.name = 'Not Right'
+ obj._SO_cacheObject.name_dirty = False
+ assert obj._SO_cacheObject.name == 'Not Right'
+
+def testDepSettingCacheObject():
+ obj = twos[1]
+ assert obj._SO_cacheObject.name == None
+ dep.process(obj.one, ['name'])
+ assert obj._SO_cacheObject.name == obj._get_name.func(obj)
+
+def testModificationSettingCacheObject():
+ obj = twos[2]
+ assert obj._SO_cacheObject.name == None
+ obj.one.name = 'G'
+ assert obj._SO_cacheObject.name == 'G'
+ assert obj.name == 'G'
+
+def testJoinUpdateSettingCacheObject():
+ obj = ones[0]
+ prev = obj.detailSize
+ obj.twos[0].length += 4
+ assert obj._SO_cacheObject.detailSize == prev + 4
+ assert obj.detailSize == prev + 4
+
+def testJoinAdditionSettingCacheObject():
+ obj = ones[1]
+ prevS = obj.detailSize
+ prevC = obj.twoCount
+ new_two = MaterializedTwo(detail='f', length=4, one=obj)
+ assert obj._SO_cacheObject.detailSize == prevS + new_two.length
+ assert obj._SO_cacheObject.twoCount == prevC + 1
+ assert obj.detailSize == prevS + new_two.length
+
+def testJoinDeletionSettingCacheObject():
+ obj = ones[1]
+ prevS = obj.detailSize
+ prevC = obj.twoCount
+ new_two = MaterializedTwo(detail='g', length=5, one=obj)
+ assert obj._SO_cacheObject.detailSize == prevS + new_two.length
+ assert obj._SO_cacheObject.twoCount == prevC + 1
+ new_two.destroySelf()
+ assert obj.twos.count() == prevC
+ assert obj._SO_cacheObject.detailSize == prevS
+ assert obj._SO_cacheObject.twoCount == prevC
\ No newline at end of file
|