]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Adjusted the attribute instrumentation change from 0.5.1 to
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 22 Jan 2009 03:55:48 +0000 (03:55 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 22 Jan 2009 03:55:48 +0000 (03:55 +0000)
fully establish instrumentation for subclasses where the mapper
was created after the superclass had already been fully
instrumented. [ticket:1292]

CHANGES
lib/sqlalchemy/orm/dynamic.py
lib/sqlalchemy/orm/interfaces.py
lib/sqlalchemy/orm/mapper.py
lib/sqlalchemy/orm/properties.py
lib/sqlalchemy/orm/strategies.py
test/orm/mapper.py

diff --git a/CHANGES b/CHANGES
index 81ef239da32fe9fc60705f3ce1c496f0abc01d23..0328d44fc25662158e35b2e935f2151156fe22ba 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -22,6 +22,11 @@ CHANGES
       The relation will raise an error if multiple parent-association
       events occur within Python.
 
+    - Adjusted the attribute instrumentation change from 0.5.1 to 
+      fully establish instrumentation for subclasses where the mapper
+      was created after the superclass had already been fully
+      instrumented. [ticket:1292]
+      
     - Fixed bug in delete-orphan cascade whereby two one-to-one
       relations from two different parent classes to the same target 
       class would prematurely expunge the instance.
index a46734dde883c49974f2139db58bb80001e70057..0de5b98ff59f634f414785a715cbb0adc9631120 100644 (file)
@@ -22,10 +22,11 @@ from sqlalchemy.orm.util import _state_has_identity, has_identity
 
 
 class DynaLoader(strategies.AbstractRelationLoader):
-    def init_class_attribute(self):
+    def init_class_attribute(self, mapper):
         self.is_class_level = True
 
         strategies._register_attribute(self,
+            mapper,
             useobject=True,
             impl_class=DynamicAttributeImpl, 
             target_mapper=self.parent_property.mapper, 
index 3e0fb94a40cf17be074570b8c7d6abf744519cf2..6c3c3b1baeb969791306222784b455ada4da9f01 100644 (file)
@@ -395,23 +395,35 @@ class MapperProperty(object):
     def instrument_class(self, mapper):
         raise NotImplementedError()
         
+    _compile_started = False
+    _compile_finished = False
+    
     def init(self):
-        """Called after all mappers are compiled to assemble
-        relationships between mappers, establish instrumented class
-        attributes.
+        """Called after all mappers are created to assemble
+        relationships between mappers and perform other post-mapper-creation
+        initialization steps.  
+        
         """
-
-        self._compiled = True
+        self._compile_started = True
         self.do_init()
-
+        self._compile_finished = True
+        
     def do_init(self):
-        """Perform subclass-specific initialization steps.
+        """Perform subclass-specific initialization post-mapper-creation steps.
 
         This is a *template* method called by the
-        ``MapperProperty`` object's init() method."""
-
+        ``MapperProperty`` object's init() method.
+        
+        """
         pass
-
+    
+    def post_instrument_class(self, mapper):
+        """Perform instrumentation adjustments that need to occur
+        after init() has completed.
+        
+        """
+        pass
+        
     def register_dependencies(self, *args, **kwargs):
         """Called by the ``Mapper`` in response to the UnitOfWork
         calling the ``Mapper``'s register_dependencies operation.
@@ -573,9 +585,11 @@ class StrategizedProperty(MapperProperty):
     def do_init(self):
         self.__all_strategies = {}
         self.strategy = self.__init_strategy(self.strategy_class)
-        if self.is_primary():
-            self.strategy.init_class_attribute()
 
+    def post_instrument_class(self, mapper):
+        if self.is_primary():
+            self.strategy.init_class_attribute(mapper)
+                
 def build_path(entity, key, prev=None):
     if prev:
         return prev + (entity, key)
@@ -810,7 +824,7 @@ class LoaderStrategy(object):
     def init(self):
         raise NotImplementedError("LoaderStrategy")
 
-    def init_class_attribute(self):
+    def init_class_attribute(self, mapper):
         pass
 
     def setup_query(self, context, entity, path, adapter, **kwargs):
index 6bcc89b3c23cf96d1319beed23d6d877b7017f6f..7cc6bad1253b9f452ad34f6456dbc46fdc772627 100644 (file)
@@ -620,6 +620,7 @@ class Mapper(object):
 
         if init:
             prop.init()
+            prop.post_instrument_class(self)
 
 
     def compile(self):
@@ -684,9 +685,14 @@ class Mapper(object):
         self._log("_post_configure_properties() started")
         l = [(key, prop) for key, prop in self._props.iteritems()]
         for key, prop in l:
-            if not getattr(prop, '_compiled', False):
-                self._log("initialize prop " + key)
+            self._log("initialize prop " + key)
+            
+            if not prop._compile_started:
                 prop.init()
+            
+            if prop._compile_finished:
+                prop.post_instrument_class(self)
+            
         self._log("_post_configure_properties() complete")
         self.compiled = True
             
index 22806e364ae802052c786eeeae8342775cd89e9f..f05613f5c0bad899eb75cbb294030757e37ebdd8 100644 (file)
@@ -711,6 +711,7 @@ class RelationProperty(StrategizedProperty):
         self._determine_direction()
         self._determine_local_remote_pairs()
         self._post_init()
+        super(RelationProperty, self).do_init()
 
     def _get_target(self):
         if not hasattr(self, 'mapper'):
@@ -998,7 +999,6 @@ class RelationProperty(StrategizedProperty):
                 "added to the primary mapper, i.e. the very first "
                 "mapper created for class '%s' " % (self.key, self.parent.class_.__name__, self.parent.class_.__name__))
         
-        super(RelationProperty, self).do_init()
 
     def _refers_to_parent_table(self):
         return self.parent.mapped_table is self.target or self.parent.mapped_table is self.target
index 22ef7ded25944505342e0ee9e86542ebf23e43f9..6edbd73d315260ea04eb8f567a3ee00bfeb8b0e4 100644 (file)
@@ -18,7 +18,7 @@ from sqlalchemy.orm.interfaces import (
 from sqlalchemy.orm import session as sessionlib
 from sqlalchemy.orm import util as mapperutil
 
-def _register_attribute(strategy, useobject,
+def _register_attribute(strategy, mapper, useobject,
         compare_function=None, 
         typecallable=None,
         copy_function=None, 
@@ -46,10 +46,10 @@ def _register_attribute(strategy, useobject,
     if useobject:
         attribute_ext.append(sessionlib.UOWEventHandler(prop.key))
 
-    for mapper in prop.parent.polymorphic_iterator():
-        if (mapper is prop.parent or not mapper.concrete) and mapper.has_property(prop.key):
+    for m in mapper.polymorphic_iterator():
+        if (m is prop.parent or not m.concrete) and m.has_property(prop.key):
             attributes.register_attribute_impl(
-                mapper.class_, 
+                m.class_, 
                 prop.key, 
                 parent_token=prop,
                 mutable_scalars=mutable_scalars,
@@ -98,12 +98,12 @@ class ColumnLoader(LoaderStrategy):
                 c = adapter.columns[c]
             column_collection.append(c)
         
-    def init_class_attribute(self):
+    def init_class_attribute(self, mapper):
         self.is_class_level = True
         coltype = self.columns[0].type
         active_history = self.columns[0].primary_key  # TODO: check all columns ?  check for foreign Key as well?
 
-        _register_attribute(self, useobject=False,
+        _register_attribute(self, mapper, useobject=False,
             compare_function=coltype.compare_values,
             copy_function=coltype.copy_value,
             mutable_scalars=self.columns[0].type.is_mutable(),
@@ -137,7 +137,7 @@ log.class_logger(ColumnLoader)
 class CompositeColumnLoader(ColumnLoader):
     """Strategize the loading of a composite column-based MapperProperty."""
 
-    def init_class_attribute(self):
+    def init_class_attribute(self, mapper):
         self.is_class_level = True
         self.logger.info("%s register managed composite attribute" % self)
 
@@ -158,7 +158,7 @@ class CompositeColumnLoader(ColumnLoader):
             else:
                 return True
 
-        _register_attribute(self, useobject=False,
+        _register_attribute(self, mapper, useobject=False,
             compare_function=compare,
             copy_function=copy,
             mutable_scalars=True
@@ -220,10 +220,10 @@ class DeferredColumnLoader(LoaderStrategy):
         self.columns = self.parent_property.columns
         self.group = self.parent_property.group
 
-    def init_class_attribute(self):
+    def init_class_attribute(self, mapper):
         self.is_class_level = True
     
-        _register_attribute(self, useobject=False,
+        _register_attribute(self, mapper, useobject=False,
              compare_function=self.columns[0].type.compare_values,
              copy_function=self.columns[0].type.copy_value,
              mutable_scalars=self.columns[0].type.is_mutable(),
@@ -335,10 +335,10 @@ class AbstractRelationLoader(LoaderStrategy):
 class NoLoader(AbstractRelationLoader):
     """Strategize a relation() that doesn't load data automatically."""
 
-    def init_class_attribute(self):
+    def init_class_attribute(self, mapper):
         self.is_class_level = True
 
-        _register_attribute(self, 
+        _register_attribute(self, mapper,
             useobject=True, 
             uselist=self.parent_property.uselist,
             typecallable = self.parent_property.collection_class,
@@ -372,11 +372,12 @@ class LazyLoader(AbstractRelationLoader):
         if self.use_get:
             self.logger.info("%s will use query.get() to optimize instance loads" % self)
 
-    def init_class_attribute(self):
+    def init_class_attribute(self, mapper):
         self.is_class_level = True
         
         
         _register_attribute(self, 
+                mapper,
                 useobject=True,
                 callable_=self.class_level_loader,
                 uselist = self.parent_property.uselist,
@@ -600,8 +601,8 @@ class EagerLoader(AbstractRelationLoader):
         super(EagerLoader, self).init()
         self.join_depth = self.parent_property.join_depth
 
-    def init_class_attribute(self):
-        self.parent_property._get_strategy(LazyLoader).init_class_attribute()
+    def init_class_attribute(self, mapper):
+        self.parent_property._get_strategy(LazyLoader).init_class_attribute(mapper)
         
     def setup_query(self, context, entity, path, adapter, column_collection=None, parentmapper=None, **kwargs):
         """Add a left outer join to the statement thats being constructed."""
index 72e417d26a41966063f3a821961529c6e5fa1571..f1f5f4a472112db743e238a232a7bf5a363535f0 100644 (file)
@@ -210,6 +210,14 @@ class MapperTest(_fixtures.FixtureTest):
         mapper(Foo, addresses, inherits=User)
         assert getattr(Foo().__class__, 'name').impl is not None
 
+    @testing.resolve_artifact_names
+    def test_deferred_subclass_attribute_instrument(self):
+        class Foo(User):pass
+        mapper(User, users)
+        compile_mappers()
+        mapper(Foo, addresses, inherits=User)
+        assert getattr(Foo().__class__, 'name').impl is not None
+
     @testing.resolve_artifact_names
     def test_compile_on_get_props_1(self):
         m =mapper(User, users)
@@ -223,7 +231,7 @@ class MapperTest(_fixtures.FixtureTest):
         assert not m.compiled
         assert m.get_property('name')
         assert m.compiled
-
+        
     @testing.resolve_artifact_names
     def test_add_property(self):
         assert_col = []