]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
modified 'primarytable' idea and 'inheritance' to work better with improved relational
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 8 Jan 2006 01:31:23 +0000 (01:31 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 8 Jan 2006 01:31:23 +0000 (01:31 +0000)
algrebra stuff

lib/sqlalchemy/mapping/mapper.py
lib/sqlalchemy/mapping/properties.py

index ee8f8ddc76f32390ddcbbdf50f903bedd71e7931..746ddb8b92665f4a33f171c0218ef65f1238b559 100644 (file)
@@ -10,7 +10,7 @@ import sqlalchemy.schema as schema
 import sqlalchemy.engine as engine
 import sqlalchemy.util as util
 import objectstore
-
+import sys
 
 mapper_registry = {}
 
@@ -35,7 +35,6 @@ class Mapper(object):
         self.copyargs = {
             'class_':class_,
             'table':table,
-            'primarytable':primarytable,
             'properties':properties or {},
             'primary_key':primary_key,
             'is_primary':None,
@@ -44,7 +43,10 @@ class Mapper(object):
             'extension':extension,
             'order_by':order_by
         }
-        
+
+        if primarytable is not None:
+            sys.stderr.write("'primarytable' argument to mapper is deprecated\n")
+            
         if extension is None:
             self.extension = MapperExtension()
         else:
@@ -55,19 +57,24 @@ class Mapper(object):
         self.order_by = order_by
         
         if not issubclass(class_, object):
-            raise "Class '%s' is not a new-style class" % class_.__name__
-
-        if inherits is not None:
-            primarytable = inherits.primarytable
-            # inherit_condition is optional since the join can figure it out
-            table = sql.join(table, inherits.table, inherit_condition)
-        
+            raise TypeError("Class '%s' is not a new-style class" % class_.__name__)
+            
         if isinstance(table, sql.Select):
             # some db's, noteably postgres, dont want to select from a select
-            # without an alias
-            self.table = table.alias(None)
+            # without an alias.  also if we make our own alias internally, then
+            # the configured properties on the mapper are not matched against the alias 
+            # we make, theres workarounds but it starts to get really crazy (its crazy enough
+            # the SQL that gets generated) so just require an alias
+            raise TypeError("Mapping against a Select object requires that it has a name.  Use an alias to give it a name, i.e. s = select(...).alias('myselect')")
         else:
             self.table = table
+
+        if inherits is not None:
+            self.primarytable = inherits.primarytable
+            # inherit_condition is optional since the join can figure it out
+            self.table = sql.join(table, inherits.table, inherit_condition)
+        else:
+            self.primarytable = self.table
         
         # locate all tables contained within the "table" passed in, which
         # may be a join or other construct
@@ -75,13 +82,6 @@ class Mapper(object):
         self.table.accept_visitor(tf)
         self.tables = tf.tables
 
-        # determine "primary" table        
-        if primarytable is None:
-            if len(self.tables) > 1:
-                raise "table contains multiple tables - specify primary table argument to Mapper"
-            self.primarytable = self.tables[0]
-        else:
-            self.primarytable = primarytable
 
         # determine primary key columns, either passed in, or get them from our set of tables
         self.pks_by_table = {}
@@ -98,7 +98,7 @@ class Mapper(object):
                 except KeyError:
                     l = self.pks_by_table.setdefault(t, util.HashSet(ordered=True))
                 if not len(t.primary_key):
-                    raise "Table " + t.name + " has no primary key columns. Specify primary_key argument to mapper."
+                    raise ValueError("Table " + t.name + " has no primary key columns. Specify primary_key argument to mapper.")
                 for k in t.primary_key:
                     l.append(k)
 
@@ -118,9 +118,17 @@ class Mapper(object):
         if properties is not None:
             for key, prop in properties.iteritems():
                 if sql.is_column(prop):
+                    try:
+                        prop = self.table._get_col_by_original(prop)
+                    except KeyError:
+                        raise ValueError("Column '%s' is not represented in mapper's table" % prop._label)
                     self.columns[key] = prop
                     prop = ColumnProperty(prop)
                 elif isinstance(prop, list) and sql.is_column(prop[0]):
+                    try:
+                        prop = [self.table._get_col_by_original(p) for p in prop]
+                    except KeyError, e:
+                        raise ValueError("Column '%s' is not represented in mapper's table" % e.args[0])
                     self.columns[key] = prop[0]
                     prop = ColumnProperty(*prop)
                 self.props[key] = prop
@@ -154,7 +162,7 @@ class Mapper(object):
             proplist.append(prop)
 
         self._get_clause = sql.and_()
-        for primary_key in self.pks_by_table[self.primarytable]:
+        for primary_key in self.pks_by_table[self.table]:
             self._get_clause.clauses.append(primary_key == sql.bindparam("pk_"+primary_key.key))
 
         if (
@@ -248,22 +256,26 @@ class Mapper(object):
             return [result] + otherresults
         else:
             return result
-
+            
     def get(self, *ident):
         """returns an instance of the object based on the given identifier, or None
         if not found.  The *ident argument is a 
         list of primary key columns in the order of the table def's primary key columns."""
         key = objectstore.get_id_key(ident, self.class_, self.primarytable)
         #print "key: " + repr(key) + " ident: " + repr(ident)
+        return self._get(key, ident)
+        
+    def _get(self, key, ident=None):
         try:
             return objectstore.uow()._get(key)
         except KeyError:
+            if ident is None:
+                ident = key[2]
             i = 0
             params = {}
             for primary_key in self.pks_by_table[self.primarytable]:
                 params["pk_"+primary_key.key] = ident[i]
                 i += 1
-            print str(self._get_clause)
             try:
                 return self.select(self._get_clause, params=params)[0]
             except IndexError:
index 2e1b7929d96c22a8c58164330ba7d7aaa75b0643..eb575d1dc47ae381b63c6ec8a7b75cfdfdb0a122 100644 (file)
@@ -66,10 +66,22 @@ class DeferredColumnProperty(ColumnProperty):
     def _copy(self):
         return DeferredColumnProperty(*self.columns)
 
+    def init(self, key, parent):
+        self.key = key
+        self.parent = parent
+        # establish a SmartProperty property manager on the object for this key, 
+        # containing a callable to load in the attribute
+        if parent._is_primary_mapper():
+            objectstore.uow().register_attribute(parent.class_, key, uselist=False, callable_=lambda i:self.setup_loader(i))
+
     def setup_loader(self, instance):
         def lazyload():
             clause = sql.and_()
-            for primary_key in self.parent.pks_by_table[self.parent.primarytable]:
+            try:
+                pk = self.parent.pks_by_table[self.columns[0].table]
+            except KeyError:
+                pk = self.columns[0].table.primary_key
+            for primary_key in pk:
                 attr = self.parent._getattrbycolumn(instance, primary_key)
                 if not attr:
                     return None
@@ -96,14 +108,6 @@ class DeferredColumnProperty(ColumnProperty):
 
     def setup(self, key, statement, **options):
         pass
-        
-    def init(self, key, parent):
-        self.key = key
-        self.parent = parent
-        # establish a SmartProperty property manager on the object for this key, 
-        # containing a callable to load in the attribute
-        if parent._is_primary_mapper():
-            objectstore.uow().register_attribute(parent.class_, key, uselist=False, callable_=lambda i:self.setup_loader(i))
 
     def execute(self, instance, row, identitykey, imap, isnew):
         if isnew:
@@ -224,7 +228,8 @@ class PropertyLoader(MapperProperty):
         objectstore.uow().register_attribute(class_, key, uselist = self.uselist, deleteremoved = self.private, extension=self.attributeext)
         
     def _get_direction(self):
-        if self.parent.primarytable is self.target:
+#        print self.key, repr(self.parent.table.name), repr(self.parent.primarytable.name), repr(self.foreignkey.table.name)
+        if self.parent.table is self.target:
             if self.foreignkey.primary_key:
                 return PropertyLoader.RIGHT
             else:
@@ -233,7 +238,7 @@ class PropertyLoader(MapperProperty):
             return PropertyLoader.CENTER
         elif self.foreignkey.table == self.target:
             return PropertyLoader.LEFT
-        elif self.foreignkey.table == self.parent.primarytable:
+        elif self.foreignkey.table == self.parent.table:
             return PropertyLoader.RIGHT
         else:
             raise "Cant determine relation direction"
@@ -297,7 +302,9 @@ class PropertyLoader(MapperProperty):
         self.primaryjoin.accept_visitor(processor)
         if self.secondaryjoin is not None:
             self.secondaryjoin.accept_visitor(processor)
-
+        if len(self.syncrules) == 0:
+            raise "No syncrules generated for join criterion " + str(self.primaryjoin)
+            
     def get_criterion(self, key, value):
         """given a key/value pair, determines if this PropertyLoader's mapper contains a key of the
         given name in its property list, or if this PropertyLoader's association mapper, if any, 
@@ -362,17 +369,6 @@ class PropertyLoader(MapperProperty):
         if self.association is not None:
             # association object.  our mapper should be dependent on both
             # the parent mapper and the association object mapper.
-
-            # this seems to work, association->parent->self, then 
-            # we process the child elements after the 'parent' save.  but
-            # then the parent is dependent on the association which is 
-            # somewhat arbitrary, might compete with some other dependency:
-    #        uowcommit.register_dependency(self.association, self.parent)
-    #        uowcommit.register_dependency(self.parent, self.mapper)
-    #       #uowcommit.register_dependency(self.association, self.mapper)
-    #        uowcommit.register_processor(self.parent, self, self.parent, False)
-    #        uowcommit.register_processor(self.parent, self, self.parent, True)
-
             # this is where we put the "stub" as a marker, so we get
             # association/parent->stub->self, then we process the child
             # elments after the 'stub' save, which is before our own
@@ -599,8 +595,8 @@ class LazyLoader(PropertyLoader):
                 # to possibly save a DB round trip
                 if self.use_get:
                     ident = []
-                    for primary_key in self.mapper.pks_by_table[self.mapper.primarytable]:
-                        ident.append(params[self.mapper.primarytable.name + "_" + primary_key.key])
+                    for primary_key in self.mapper.pks_by_table[self.mapper.table]:
+                        ident.append(params[self.mapper.table.name + "_" + primary_key.key])
                     return self.mapper.get(*ident)
                 elif self.order_by is not False:
                     order_by = self.order_by
@@ -673,7 +669,8 @@ class EagerLoader(PropertyLoader):
         if self.secondaryjoin is not None:
             [self.to_alias.append(f) for f in self.secondaryjoin._get_from_objects()]
         try:
-            del self.to_alias[parent.primarytable]
+#            del self.to_alias[parent.primarytable]
+            del self.to_alias[parent.table]
         except KeyError:
             pass