]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
[ticket:309]
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 29 Sep 2006 20:41:37 +0000 (20:41 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 29 Sep 2006 20:41:37 +0000 (20:41 +0000)
lib/sqlalchemy/orm/mapper.py
lib/sqlalchemy/orm/properties.py
test/orm/mapper.py

index 29688df21e282d9ede95a0824366a85b657f8af9..24225bd6d33a8d10bb12f2806b3a5b3e100e9c08 100644 (file)
@@ -517,8 +517,10 @@ class Mapper(object):
             self.add_property(key, value)
 
     def add_property(self, key, prop):
-        """adds an indiviual MapperProperty to this mapper.  If the mapper has not been compiled yet,
-        just adds the property to the initial properties dictionary sent to the constructor.  if this Mapper
+        """add an indiviual MapperProperty to this mapper.  
+        
+        If the mapper has not been compiled yet, just adds the property to the initial 
+        properties dictionary sent to the constructor.  if this Mapper
         has already been compiled, then the given MapperProperty is compiled immediately."""
         self.properties[key] = prop
         if self.__is_compiled:
@@ -547,13 +549,14 @@ class Mapper(object):
         else:
             return None
 
-    def _compile_property(self, key, prop, init=True, skipmissing=False):
-        """adds an additional property to this mapper.  this is the same as if it were 
-        specified within the 'properties' argument to the constructor.  if the named
-        property already exists, this will replace it.  Useful for
-        circular relationships, or overriding the parameters of auto-generated properties
-        such as backreferences."""
-
+    def _compile_property(self, key, prop, init=True, skipmissing=False, localparent=None):
+        """add a MapperProperty to this or another Mapper, including configuration of the property.
+        
+        The properties' parent attribute will be set, and the property will also be 
+        copied amongst the mappers which inherit from this one.
+        
+        if the given prop is a Column or list of Columns, a ColumnProperty will be created.
+        """
         self.__log("_compile_property(%s, %s)" % (key, prop.__class__.__name__))
 
         if not isinstance(prop, MapperProperty):
@@ -561,9 +564,10 @@ class Mapper(object):
             if prop is None:
                 raise exceptions.ArgumentError("'%s' is not an instance of MapperProperty or Column" % repr(prop))
 
-        self.__props[key] = prop
+        effectiveparent = localparent or self
+        effectiveparent.__props[key] = prop
         prop.set_parent(self)
-        
+            
         if isinstance(prop, ColumnProperty):
             col = self.select_table.corresponding_column(prop.columns[0], keys_ok=True, raiseerr=False)
             if col is None:
@@ -574,11 +578,11 @@ class Mapper(object):
                 proplist.append(prop)
 
         if init:
-            prop.init(key, self)
+            prop.init(key, effectiveparent)
 
-        for mapper in self._inheriting_mappers:
+        for mapper in effectiveparent._inheriting_mappers:
             prop.adapt_to_inherited(key, mapper)
-        
+
     def __str__(self):
         return "Mapper|" + self.class_.__name__ + "|" + (self.entity_name is not None and "/%s" % self.entity_name or "") + (self.local_table and self.local_table.name or str(self.local_table)) + (not self._is_primary_mapper() and "|non-primary" or "")
     
@@ -1034,6 +1038,7 @@ class Mapper(object):
         identitykey = self._row_identity_key(row)
         if session.has_key(identitykey):
             instance = session._get(identitykey)
+            self.__log_debug("_instance(): using existing instance %s identity %s" % (mapperutil.instance_str(instance), str(identitykey)))
             isnew = False
             if version_check and self.version_id_col is not None and self._getattrbycolumn(instance, self.version_id_col) != row[self.version_id_col]:
                 raise exceptions.ConcurrentModificationError("Instance '%s' version of %s does not match %s" % (instance, self._getattrbycolumn(instance, self.version_id_col), row[self.version_id_col]))
@@ -1070,7 +1075,7 @@ class Mapper(object):
             instance = self.extension.create_instance(self, session, row, imap, self.class_)
             if instance is EXT_PASS:
                 instance = self._create_instance(session)
-            self.__log_debug("new instance %s identity %s" % (mapperutil.instance_str(instance), str(identitykey)))
+            self.__log_debug("_instance(): created new instance %s identity %s" % (mapperutil.instance_str(instance), str(identitykey)))
             imap[identitykey] = instance
             isnew = True
         else:
index 6e9f9daff5b37f1c143440820b3e1174fa79549f..58debab0257f442fc8db1872dac1d67b7effa49c 100644 (file)
@@ -377,6 +377,7 @@ PropertyLoader.logger = logging.class_logger(PropertyLoader)
 
 class LazyLoader(PropertyLoader):
     def do_init_subclass(self):
+        print "LAZYLOADER %d DOINITSUBCLASS" % id(self)
         (self.lazywhere, self.lazybinds, self.lazyreverse) = create_lazy_clause(self.parent.unjoined_table, self.primaryjoin, self.secondaryjoin, self.foreignkey)
         # determine if our "lazywhere" clause is the same as the mapper's
         # get() clause.  then we can just use mapper.get()
@@ -496,6 +497,7 @@ def create_lazy_clause(table, primaryjoin, secondaryjoin, foreignkey):
     lazywhere.accept_visitor(li)
     if secondaryjoin is not None:
         lazywhere = sql.and_(lazywhere, secondaryjoin)
+    LazyLoader.logger.debug("create_lazy_clause " + str(lazywhere))
     return (lazywhere, binds, reverse)
         
 
@@ -808,8 +810,12 @@ class EagerLazyOption(GenericOption):
         oldprop = mapper.props[key]
         newprop = class_.__new__(class_)
         newprop.__dict__.update(oldprop.__dict__)
-        newprop.do_init_subclass()
-        mapper._compile_property(key, newprop)
+        #newprop.do_init_subclass()
+        p = newprop
+        while p.inherits is not None:
+            p = p.inherits
+        real_parent_mapper = p.parent
+        real_parent_mapper._compile_property(key, newprop, localparent=mapper)
 
 class DeferredOption(GenericOption):
     def __init__(self, key, defer=False, **kwargs):
index 12059d28e228f23ed563320829a2eafa69fd3fbf..c22319ec8961551ae6c5a51c1a5584583ec48efa 100644 (file)
@@ -502,6 +502,32 @@ class InheritanceTest(MapperSuperTest):
         sess.clear()
         au = sess.query(usermapper).get_by(user_name='jack')
         self.assert_(au.email_address == 'jack@gmail.com')
+
+    def testlazyoption(self):
+        """test that a lazy options gets created against its correct mapper when
+        using options with inheriting mappers"""
+        class _Order(object):
+            pass
+        class _User(object):
+            pass
+        class AddressUser(_User):
+            pass
+        ordermapper = mapper(_Order, orders)
+        usermapper = mapper(_User, users, 
+            properties = {
+                'orders' : relation(ordermapper, lazy=True)
+            })
+        amapper = mapper(AddressUser, addresses, inherits = usermapper)
+            
+        sess = create_session()
+
+        def go():
+            l = sess.query(AddressUser).options(lazyload('orders')).select()
+            # this would fail because the "orders" lazyloader gets created against AddressUsers selectable
+            # and not _User's.
+            assert len(l[0].orders) == 3
+        self.assert_sql_count(db, go, 2)
+        
             
     
 class DeferredTest(MapperSuperTest):