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.
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,
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.
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)
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):
if init:
prop.init()
+ prop.post_instrument_class(self)
def compile(self):
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
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'):
"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
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,
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,
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(),
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)
else:
return True
- _register_attribute(self, useobject=False,
+ _register_attribute(self, mapper, useobject=False,
compare_function=compare,
copy_function=copy,
mutable_scalars=True
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(),
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,
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,
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."""
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)
assert not m.compiled
assert m.get_property('name')
assert m.compiled
-
+
@testing.resolve_artifact_names
def test_add_property(self):
assert_col = []