import random, copy, types
__ALL__ = ['eagermapper', 'eagerloader', 'lazymapper', 'lazyloader', 'eagerload', 'lazyload', 'assignmapper',
- 'mapper', 'lazyloader', 'lazymapper', 'clear_mappers', 'objectstore', 'sql', 'MapperExtension']
+ 'mapper', 'lazyloader', 'lazymapper', 'clear_mappers', 'objectstore', 'sql', 'extension', 'MapperExtension']
def relation(*args, **params):
"""provides a relationship of a primary Mapper to a secondary Mapper, which corresponds
created for the previous mapper's class, it will be used as that classes'
new primary mapper."""
del _mappers[m.hash_key]
-
+
+def extension(ext):
+ """returns a MapperOption that will add the given MapperExtension to the
+ mapper returned by mapper.options()."""
+ return ExtensionOption(ext)
def eagerload(name):
"""returns a MapperOption that will convert the property of the given name
into an eager load. Used with mapper.options()"""
def instance_key(self, instance):
return self.identity_key(*[self._getattrbycolumn(instance, column) for column in self.primary_keys[self.table]])
+# def _primary_key_ident(self, obj):
+# """returns an identity of an object based on its primary keys, across all tables
+# represented by this mapper."""
+# res = []
+# for table in self.tables:
+# for k in self.primary_keys[table]:
+# res.append(self._getattrbycolumn(obj, k))
+# return tuple(res)
+
def compile(self, whereclause = None, **options):
"""works like select, except returns the SQL statement object without
compiling or executing it"""
def _setattrbycolumn(self, obj, column, value):
self.columntoproperty[column][0].setattr(obj, value)
+
def save_obj(self, objects, uow):
"""called by a UnitOfWork object to save objects, which involves either an INSERT or
an UPDATE statement for each table used by this mapper, for each element of the
for col in self.primary_keys[table]:
params[col.key] = self._getattrbycolumn(obj, col)
uow.register_deleted_object(obj)
+ self.extension.before_delete(self, obj)
if len(delete):
clause = sql.and_()
for col in self.primary_keys[table]:
- print "adding clause for primary key", table.name, "col", col.key
clause.clauses.append(col == sql.bindparam(col.key))
- print "so heres the clause", str(clause)
statement = table.delete(clause)
c = statement.execute(*delete)
if c.rowcount != len(delete):
"""describes an object property that holds a single item or list of items that correspond
to a related database table."""
- def __init__(self, argument, secondary, primaryjoin, secondaryjoin, foreignkey = None, uselist = None, private = False, thiscol = None, live=False, isoption=False, **kwargs):
+ def __init__(self, argument, secondary, primaryjoin, secondaryjoin, foreignkey=None, uselist=None, private=False, live=False, isoption=False, isassociation=False, **kwargs):
self.uselist = uselist
self.argument = argument
self.secondary = secondary
self.secondaryjoin = secondaryjoin
self.foreignkey = foreignkey
self.private = private
- self.thiscol = thiscol
self.live = live
self.isoption = isoption
+ self.isassociation = isassociation
self._hash_key = "%s(%s, %s, %s, %s, %s, %s, %s)" % (self.__class__.__name__, hash_key(self.argument), hash_key(secondary), hash_key(primaryjoin), hash_key(secondaryjoin), hash_key(foreignkey), repr(uselist), repr(private))
def _copy(self):
# gets added to the "todo" list
uowcommit.register_dependency(self.mapper, self.parent)
uowcommit.register_processor(self.parent, self, self.parent, False)
+ uowcommit.register_processor(self.parent, self, self.parent, True)
elif self.direction == PropertyLoader.LEFT:
uowcommit.register_dependency(self.parent, self.mapper)
uowcommit.register_processor(self.parent, self, self.parent, False)
self._synchronize(obj, child, associationrow, False)
secondary_delete.append(associationrow)
uowcommit.register_saved_list(childlist)
- if len(secondary_delete):
- # TODO: precompile the delete/insert queries and store them as instance variables
- # on the PropertyLoader
- statement = self.secondary.delete(sql.and_(*[c == sql.bindparam(c.key) for c in self.secondary.c]))
- statement.execute(*secondary_delete)
- if len(secondary_insert):
- statement = self.secondary.insert()
- statement.execute(*secondary_insert)
+ if len(secondary_delete):
+ # TODO: precompile the delete/insert queries and store them as instance variables
+ # on the PropertyLoader
+ statement = self.secondary.delete(sql.and_(*[c == sql.bindparam(c.key) for c in self.secondary.c]))
+ statement.execute(*secondary_delete)
+ if len(secondary_insert):
+ statement = self.secondary.insert()
+ statement.execute(*secondary_insert)
elif self.direction == PropertyLoader.RIGHT and delete:
# head object is being deleted, and we manage a foreign key object.
# dont have to do anything to it.
self._synchronize(obj, child, None, True)
uowcommit.register_object(child)
uowcommit.register_deleted_list(childlist)
+ elif self.isassociation:
+ for obj in deplist:
+ childlist = getlist(obj, passive=True)
+ if childlist is None: continue
+ uowcommit.register_saved_list(childlist)
+
+ # TODO: sort out the association objects so that we only insert/delete/update those
+ # that are actually correct.
+ for child in childlist:
+ self._synchronize(obj, child, None, False)
+ uowcommit.unregister_object(child)
+
+ for child in childlist.deleted_items():
+ uowcommit.unregister_object(child)
+
else:
for obj in deplist:
#print "PROCESS:", repr(obj)
self.mapper._instance(row, imap, result_list)
-class MapperOption:
+class MapperOption(object):
"""describes a modification to a Mapper in the context of making a copy
of it. This is used to assist in the prototype pattern used by mapper.options()."""
def process(self, mapper):
def hash_key(self):
return repr(self)
+class ExtensionOption(MapperOption):
+ """adds a new MapperExtension to a mapper's chain of extensions"""
+ def __init__(self, ext):
+ self.ext = ext
+ def process(self, mapper):
+ ext.next = mapper.extension
+ mapper.extension = ext
+
class EagerLazyOption(MapperOption):
"""an option that switches a PropertyLoader to be an EagerLoader or LazyLoader"""
def __init__(self, key, toeager = True):
class MapperExtension(object):
+ def __init__(self):
+ self.next = None
def create_instance(self, mapper, row, imap, class_):
- return None
+ if self.next is None:
+ return None
+ else:
+ return self.next.create_instance(mapper, row, imap, class_)
def append_result(self, mapper, row, imap, result, instance, isnew, populate_existing=False):
- return True
+ if self.next is None:
+ return True
+ else:
+ return self.next.append_result(mapper, row, imap, result, instance, isnew, populate_existing)
def after_insert(self, mapper, instance):
- pass
+ if self.next is not None:
+ self.next.after_insert(mapper, instance)
+ def before_delete(self, mapper, instance):
+ if self.next is not None:
+ self.next.before_delete(mapper, instance)
def hash_key(obj):
if obj is None: