]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
(no commit message)
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 21 Jul 2005 04:05:01 +0000 (04:05 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 21 Jul 2005 04:05:01 +0000 (04:05 +0000)
lib/sqlalchemy/databases/sqlite.py
lib/sqlalchemy/mapper.py

index e5577d7f3a75e48551b6f6727a2f84d98b2d1056..d989d1d663710c9bc25b860b1cde634d827de08c 100644 (file)
@@ -57,6 +57,9 @@ class SQLiteSQLEngine(ansisql.ANSISQLEngine):
     def columnimpl(self, column):
         return SQLiteColumnImpl(column)
 
+    def reflecttable(self, table):
+        raise NotImplementedError()
+
 class SQLiteColumnImpl(sql.ColumnSelectable):
     def _get_specification(self):
         coltype = self.column.type
index 4f991be3cc450611cc6b150fbf43183bd76d18d4..583c84a7d055f0bd1fbdb283ee9c1cccbb6d57e6 100644 (file)
@@ -27,16 +27,23 @@ usermapper.select("user_id LIKE "%foo%")
 import sqlalchemy.sql as sql
 import sqlalchemy.schema as schema
 import sqlalchemy.engine as engine
-import weakref, random
+import sqlalchemy.util as util
+import weakref, random, copy
 
-__ALL__ = ['eagermapper', 'eagerloader', 'mapper', 'lazyloader', 'lazymapper', 'identitymap', 'globalidentity']
+__ALL__ = ['eagermapper', 'eagerloader', 'lazymapper', 'lazyloader', 'eagerload', 'lazyload', 'mapper', 'lazyloader', 'lazymapper', 'identitymap', 'globalidentity']
 
+def lazymapper(class_, selectable, whereclause, table = None, properties = None, **options):
+    return lazyloader(mapper(class_, selectable, table = table, properties = properties, isroot = False), whereclause, **options)
+    
 def eagermapper(class_, selectable, whereclause, table = None, properties = None, **options):
     return eagerloader(mapper(class_, selectable, table = table, properties = properties, isroot = False), whereclause, **options)
 
 def eagerloader(mapper, whereclause, **options):
     return EagerLoader(mapper, whereclause, **options)
 
+def lazyloader(mapper, whereclause, **options):
+    return LazyLoader(mapper, whereclause, **options)
+
 def mapper(class_, selectable, table = None, properties = None, identitymap = None, use_smart_properties = True, isroot = True):
     return Mapper(class_, selectable, table = table, properties = properties, identitymap = identitymap, use_smart_properties = use_smart_properties, isroot = isroot)
 
@@ -45,6 +52,12 @@ def identitymap():
 
 def globalidentity():
     return _global_identitymap
+    
+def eagerload(name):
+    return EagerOption(name)
+    
+def lazyload(name):
+    return LazyOption(name)
 
 class Mapper(object):
     def __init__(self, class_, selectable, table = None, properties = None, identitymap = None, use_smart_properties = True, isroot = True):
@@ -72,7 +85,12 @@ class Mapper(object):
         if isroot:
             self.init(self)
     
+    def set_property(self, key, prop):
+        self.props[key] = prop
+        prop.init(key, self, self.root)
+        
     def init(self, root):
+        self.root = root
         self.identitymap = root.identitymap
         [prop.init(key, self, root) for key, prop in self.props.iteritems()]
             
@@ -92,6 +110,17 @@ class Mapper(object):
     def get(self, id):
         """returns an instance of the object based on the given ID."""
         pass
+
+    def compile(self, whereclause = None, **options):
+        """works like select, except returns the SQL statement object without 
+        compiling or executing it"""
+        return self._compile(whereclause, **options)
+    
+    def options(self, *options):
+        mapper = copy.copy(self)
+        for option in options:
+            option.process(mapper)
+        return mapper
         
     def select(self, arg = None, **params):
         """selects instances of the object from the database.  
@@ -117,8 +146,11 @@ class Mapper(object):
         of the attribute, determines if the item is saved.  if smart attributes are not being 
         used, the item is saved unconditionally.
         """
-        if not getattr(object, 'dirty', True):
-            return
+        if getattr(object, 'dirty', True):
+            pass
+            # do the save
+        for prop in self.props.values():
+            prop.save(object, traverse, refetch)
     
     def remove(self, object, traverse = True):
         """removes the object.  traverse indicates attached objects should be removed as well."""
@@ -149,11 +181,16 @@ class Mapper(object):
         tf = Mapper.TableFinder()
         selectable.accept_visitor(tf)
         return tf.table
-        
-    def _select_whereclause(self, whereclause = None, **params):
+
+    def _compile(self, whereclause = None, **options):
         statement = sql.select([self.selectable], whereclause)
         for key, value in self.props.iteritems():
-            value.setup(key, self.selectable, statement) 
+            value.setup(key, self.selectable, statement, **options) 
+        statement.use_labels = True
+        return statement
+        
+    def _select_whereclause(self, whereclause = None, **params):
+        statement = self._compile(whereclause)
         return self._select_statement(statement, **params)
     
     def _select_statement(self, statement, **params):
@@ -186,7 +223,7 @@ class Mapper(object):
         # call further mapper properties on the row, to pull further 
         # instances from the row and possibly populate this item.
         for key, prop in self.props.iteritems():
-            prop.execute(instance, key, row, identitykey, localmap, exists)
+            prop.execute(instance, row, identitykey, localmap, exists)
 
         # now add to the result list, but we only want to add 
         # to the result list uniquely, so get another identity map
@@ -200,24 +237,32 @@ class Mapper(object):
             result.append(instance)
 
 
-
+class MapperOption:
+    def process(self, mapper):
+        raise NotImplementedError()
+        
 class MapperProperty:
-    def execute(self, instance, key, row, isduplicate):
+    def execute(self, instance, row, isduplicate):
         """called when the mapper receives a row.  instance is the parent instance corresponding
         to the row. """
         raise NotImplementedError()
-    def setup(self, key, primarytable, statement):
-        """called when a statement is being constructed."""
-        pass
+    def setup(self, key, primarytable, statement, **options):
+        """called when a statement is being constructed.  """
+        return self
     def init(self, key, parent, root):
         """called when the MapperProperty is first attached to a new parent Mapper."""
         pass
-
+    def save(self, object, traverse, refetch):
+        pass
+    def delete(self, object):
+        pass
+        
 class ColumnProperty(MapperProperty):
     def __init__(self, column):
         self.column = column
 
     def init(self, key, parent, root):
+        self.key = key
         if root.use_smart_properties:
             self.use_smart = True
             if not hasattr(parent.class_, key):
@@ -225,30 +270,44 @@ class ColumnProperty(MapperProperty):
         else:
             self.use_smart = False
             
-    def execute(self, instance, key, row, identitykey, localmap, isduplicate):
+    def execute(self, instance, row, identitykey, localmap, isduplicate):
         if not isduplicate:
             if self.use_smart:
-                instance.__dict__[key] = row[self.column.label]
+                instance.__dict__[self.key] = row[self.column.label]
             else:
-                setattr(instance, key, row[self.column.label])
+                setattr(instance, self.key, row[self.column.label])
 
-    
-class EagerLoader(MapperProperty):
+
+class PropertyLoader(MapperProperty):
     def __init__(self, mapper, whereclause, **options):
         self.mapper = mapper
         self.whereclause = whereclause
-    
+
     def init(self, key, parent, root):
+        self.key = key
         self.mapper.init(root)
+
+    def save(self, object, traverse, refetch):
+        self.mapper.save(object, )
+    def delete(self):
+        self.mapper.delete()
         
-    def setup(self, key, primarytable, statement):
+class LazyLoader(PropertyLoader):
+    pass
+    
+class EagerLoader(PropertyLoader):
+    def setup(self, key, primarytable, statement, **options):
         """add a left outer join to the statement thats being constructed"""
         targettable = self.mapper.selectable
 
         if statement.whereclause is not None:
-            #if the whereclause of the statement contains the table we eager load against,
-            # "aliasize" the whereclause into a new selectable unit
-            for target in [targettable]: # + self.whereclause._get_from_objects():
+            # if the whereclause of the statement references tables that are also
+            # in the outer join we are constructing, then convert those objects to 
+            # reference "aliases" of those tables so that their where condition does not interfere
+            # with ours
+            targets = util.Set([targettable] + self.whereclause._get_from_objects())
+            del targets[primarytable]
+            for target in targets:
                 aliasizer = Aliasizer(target, "aliased_" + target.name + "_" + hex(random.randint(0, 65535))[2:])
                 statement.whereclause.accept_visitor(aliasizer)
                 statement.append_from(aliasizer.alias)
@@ -262,16 +321,34 @@ class EagerLoader(MapperProperty):
         for key, value in self.mapper.props.iteritems():
             value.setup(key, self.mapper.selectable, statement) 
         
-    def execute(self, instance, key, row, identitykey, localmap, isduplicate):
+    def execute(self, instance, row, identitykey, localmap, isduplicate):
         """receive a row.  tell our mapper to look for a new object instance in the row, and attach
         it to a list on the parent instance."""
         try:
-            list = getattr(instance, key)
+            list = getattr(instance, self.key)
         except AttributeError:
             list = []
-            setattr(instance, key, list)
+            setattr(instance, self.key, list)
         self.mapper._instance(row, localmap, list)
 
+class EagerOption(MapperOption):
+    """an option that switches a PropertyLoader to be an EagerLoader"""
+    def __init__(self, key):
+        self.key = key
+
+    def process(self, mapper):
+        oldprop = mapper.props[self.key]
+        mapper.set_property(self.key, EagerLoader(oldprop.mapper, oldprop.whereclause))
+        
+class LazyOption(MapperOption):
+    """an option that switches a PropertyLoader to be a LazyLoader"""
+    def __init__(self, key):
+        self.key = key
+
+    def process(self, mapper):
+        oldprop = mapper.props[self.key]
+        mapper.set_property(self.key, LazyLoader(oldprop.mapper, oldprop.whereclause))
+        
 class Aliasizer(sql.ClauseVisitor):
     def __init__(self, table, aliasname):
         self.table = table