From 68c8b13ed6b8f88d958f72f6d0721b25817c6636 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Sun, 2 Aug 2009 17:51:33 +0000 Subject: [PATCH] - Simplified the sweep of instrumentation in strategies._register_attribute - Improved support for MapperProperty objects overriding that of an inherited mapper for non-concrete inheritance setups - attribute extensions won't randomly collide with each other. [ticket:1488] - Added AttributeExtension to sqlalchemy.orm.__all__ --- CHANGES | 7 ++++ lib/sqlalchemy/orm/__init__.py | 2 ++ lib/sqlalchemy/orm/mapper.py | 3 ++ lib/sqlalchemy/orm/strategies.py | 5 +-- test/orm/test_mapper.py | 58 ++++++++++++++++++++++++++++++++ 5 files changed, 73 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 8abf88dffd..447577f9de 100644 --- a/CHANGES +++ b/CHANGES @@ -31,6 +31,13 @@ CHANGES inheritance attributes which were based on column_property() or similar would fail to evaluate. [ticket:1480] + + - Improved support for MapperProperty objects overriding + that of an inherited mapper for non-concrete + inheritance setups - attribute extensions won't randomly + collide with each other. [ticket:1488] + + - Added AttributeExtension to sqlalchemy.orm.__all__ - Improved error message when query() is called with a non-SQL /entity expression. [ticket:1476] diff --git a/lib/sqlalchemy/orm/__init__.py b/lib/sqlalchemy/orm/__init__.py index 4c4ec8513c..2a20b05ef8 100644 --- a/lib/sqlalchemy/orm/__init__.py +++ b/lib/sqlalchemy/orm/__init__.py @@ -26,6 +26,7 @@ from sqlalchemy.orm.interfaces import ( MapperExtension, PropComparator, SessionExtension, + AttributeExtension, ) from sqlalchemy.orm.util import ( AliasedClass as aliased, @@ -61,6 +62,7 @@ __all__ = ( 'EXT_STOP', 'InstrumentationManager', 'MapperExtension', + 'AttributeExtension', 'Validator', 'PropComparator', 'Query', diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index d53faacf71..078056a01c 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -649,6 +649,9 @@ class Mapper(object): return self # initialize properties on all mappers + # note that _mapper_registry is unordered, which + # may randomly conceal/reveal issues related to + # the order of mapper compilation for mapper in list(_mapper_registry): if getattr(mapper, '_compile_failed', False): raise sa_exc.InvalidRequestError("One or more mappers failed to compile. Exception was probably " diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index 1da2231a3a..f739fb1dd0 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -45,9 +45,10 @@ def _register_attribute(strategy, mapper, useobject, if useobject: attribute_ext.append(sessionlib.UOWEventHandler(prop.key)) - + for m in mapper.polymorphic_iterator(): - if (m is prop.parent or not m.concrete) and m.has_property(prop.key): + if prop is m._props.get(prop.key): + attributes.register_attribute_impl( m.class_, prop.key, diff --git a/test/orm/test_mapper.py b/test/orm/test_mapper.py index edde2a7565..13913578a5 100644 --- a/test/orm/test_mapper.py +++ b/test/orm/test_mapper.py @@ -2219,6 +2219,64 @@ class NoLoadTest(_fixtures.FixtureTest): ) +class AttributeExtensionTest(_base.MappedTest): + @classmethod + def define_tables(cls, metadata): + Table('t1', + metadata, + Column('id', Integer, primary_key=True), + Column('type', String(40)), + Column('data', String(50)) + + ) + + @testing.resolve_artifact_names + def test_cascading_extensions(self): + ext_msg = [] + + class Ex1(sa.orm.AttributeExtension): + def set(self, state, value, oldvalue, initiator): + ext_msg.append("Ex1 %r" % value) + return "ex1" + value + + class Ex2(sa.orm.AttributeExtension): + def set(self, state, value, oldvalue, initiator): + ext_msg.append("Ex2 %r" % value) + return "ex2" + value + + class A(_base.BasicEntity): + pass + class B(A): + pass + class C(B): + pass + + mapper(A, t1, polymorphic_on=t1.c.type, polymorphic_identity='a', properties={ + 'data':column_property(t1.c.data, extension=Ex1()) + }) + mapper(B, polymorphic_identity='b', inherits=A) + mc = mapper(C, polymorphic_identity='c', inherits=B, properties={ + 'data':column_property(t1.c.data, extension=Ex2()) + }) + + a1 = A(data='a1') + b1 = B(data='b1') + c1 = C(data='c1') + + eq_(a1.data, 'ex1a1') + eq_(b1.data, 'ex1b1') + eq_(c1.data, 'ex2c1') + + a1.data = 'a2' + b1.data='b2' + c1.data = 'c2' + eq_(a1.data, 'ex1a2') + eq_(b1.data, 'ex1b2') + eq_(c1.data, 'ex2c2') + + eq_(ext_msg, ["Ex1 'a1'", "Ex1 'b1'", "Ex2 'c1'", "Ex1 'a2'", "Ex1 'b2'", "Ex2 'c2'"]) + + class MapperExtensionTest(_fixtures.FixtureTest): run_inserts = None -- 2.47.2