Mike Bayer  committed 31e2617

implemented SyncRules for mapper with inheritance relationship, fixes [ticket:81]
TableFinder becomes a list-implementing object (should probably create clauseutils or sqlutils for these little helper visitors)

  • Participants
  • Parent commits 35e8e02
  • Branches default

Comments (0)

Files changed (4)

File lib/sqlalchemy/mapping/

 import sqlalchemy.schema as schema
 import sqlalchemy.engine as engine
 import sqlalchemy.util as util
+import sync
 from sqlalchemy.exceptions import *
 import objectstore
 import sys
             self.primarytable = inherits.primarytable
             # inherit_condition is optional since the join can figure it out
             self.table = sql.join(inherits.table, table, inherit_condition)
+            self._synchronizer = sync.ClauseSynchronizer(self, self, sync.ONETOMANY)
+            self._synchronizer.compile(self.table.onclause, inherits.tables, TableFinder(table))
             self.primarytable = self.table
+            self._synchronizer = None
         # locate all tables contained within the "table" passed in, which
         # may be a join or other construct
-        tf = TableFinder()
-        self.table.accept_visitor(tf)
-        self.tables = tf.tables
+        self.tables = TableFinder(self.table)
         # determine primary key columns, either passed in, or get them from our set of tables
         self.pks_by_table = {}
                     self.props[key] = prop.copy()
                     self.props[key].parent = self
                     self.props[key].key = None  # force re-init
         l = [(key, prop) for key, prop in self.props.iteritems()]
         for key, prop in l:
             if getattr(prop, 'key', None) is None:
                         for c in table.c:
                             if self._getattrbycolumn(obj, c) is None:
                                 self._setattrbycolumn(obj, c, row[c])
+                    if self._synchronizer is not None:
+                        self._synchronizer.execute(obj, obj)
                     self.extension.after_insert(self, obj)
     def delete_obj(self, objects, uow):
 class TableFinder(sql.ClauseVisitor):
     """given a Clause, locates all the Tables within it into a list."""
-    def __init__(self):
+    def __init__(self, table):
         self.tables = []
+        table.accept_visitor(self)
     def visit_table(self, table):
+    def __getitem__(self, i):
+        return self.tables[i]
+    def __iter__(self):
+        return iter(self.tables)
+    def __contains__(self, obj):
+        return obj in self.tables
+    def __add__(self, obj):
+        return self.tables + obj
 def hash_key(obj):
     if obj is None:
         return 'None'

File lib/sqlalchemy/mapping/

     def execute(self, instance, row, identitykey, imap, isnew):
         if isnew:
             instance.__dict__[self.key] = row[self.columns[0]]
+    def __repr__(self):
+        return "ColumnProperty(%s)" % repr([str(c) for c in self.columns])
 class DeferredColumnProperty(ColumnProperty):
     """describes an object attribute that corresponds to a table column, which also
     will "lazy load" its value from the table.  this is per-column lazy loading."""

File lib/sqlalchemy/mapping/

 import sqlalchemy.sql as sql
 import sqlalchemy.schema as schema
 from sqlalchemy.exceptions import *
-import properties
 """contains the ClauseSynchronizer class which is used to map attributes between two objects
 in a manner corresponding to a SQL clause that compares column values."""
         if len(self.syncrules) == rules_added:
             raise ArgumentError("No syncrules generated for join criterion " + str(sqlclause))
-    def execute(self, source, dest, obj, child, clearkeys):
+    def execute(self, source, dest, obj=None, child=None, clearkeys=None):
         for rule in self.syncrules:
             rule.execute(source, dest, obj, child, clearkeys)
         if isinstance(dest, dict):
             dest[self.dest_column.key] = value
-            #print "SYNC VALUE", value, "TO", dest
+            #print "SYNC VALUE", value, "TO", dest, self.source_column, self.dest_column
             self.dest_mapper._setattrbycolumn(dest, self.dest_column, value)
 class BinaryVisitor(sql.ClauseVisitor):

File test/

                 return "Bar(%s)" %
         Bar.mapper = mapper(Bar, bar, inherits=Foo.mapper, properties = {
-                # TODO: use syncrules for this
-                'id':[,]
+                # the old way, you needed to explicitly set up a compound
+                # column like this.  but now the mapper uses SyncRules to match up
+                # the parent/child inherited columns
+                #'id':[,]
         Bar.mapper.add_property('foos', relation(Foo.mapper, foo_bar,,, lazy=False))