From: Mike Bayer Date: Fri, 8 Aug 2008 05:13:23 +0000 (+0000) Subject: - Fixed @on_reconsitute hook for subclasses X-Git-Tag: rel_0_5rc1~84 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=29d4335b73b2b81e941050f0395aa4fb4e58cd39;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - Fixed @on_reconsitute hook for subclasses which inherit from a base class. [ticket:1129] --- diff --git a/CHANGES b/CHANGES index a5258ceacf..30b0baae5d 100644 --- a/CHANGES +++ b/CHANGES @@ -21,6 +21,10 @@ CHANGES list of entities. In particular scalar subqueries should not "leak" their inner FROM objects out into the enclosing query. + + - Fixed @on_reconsitute hook for subclasses + which inherit from a base class. + [ticket:1129] - sql - Temporarily rolled back the "ORDER BY" enhancement diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py index 5904247596..ee05625a91 100644 --- a/lib/sqlalchemy/orm/attributes.py +++ b/lib/sqlalchemy/orm/attributes.py @@ -26,6 +26,7 @@ from sqlalchemy import util from sqlalchemy.util import EMPTY_SET from sqlalchemy.orm import interfaces, collections, exc import sqlalchemy.exceptions as sa_exc +import types # lazy imports _entity_info = None @@ -1055,9 +1056,15 @@ class ClassManager(dict): self._instantiable = False self.events = self.event_registry_factory() - for meth in class_.__dict__.values(): - if hasattr(meth, '_sa_reconstitute'): - self.events.add_listener('on_load', meth) + # TODO: generalize (and document the rationalization for) this traversal. + # TODO: figure out why getattr(cls, key) for all attributes + # causes test failures + for cls in class_.__mro__[0:-1]: + for key, meth in cls.__dict__.iteritems(): + if isinstance(meth, types.FunctionType) and \ + hasattr(meth, '__sa_reconstitute__') and \ + hasattr(getattr(class_, key), '__sa_reconstitute__'): + self.events.add_listener('on_load', meth) def instantiable(self, boolean): # experiment, probably won't stay in this form @@ -1473,7 +1480,7 @@ def on_reconstitute(fn): to MapperExtension.on_reconstitute(). """ - fn._sa_reconstitute = True + fn.__sa_reconstitute__ = True return fn diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index 732e65fb73..06f4f3dadf 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -618,6 +618,7 @@ class Mapper(object): cls = object.__getattribute__(self, 'class_') clskey = object.__getattribute__(self, 'key') + # ugly hack if key.startswith('__') and key != '__clause_element__': return object.__getattribute__(self, key) diff --git a/test/orm/extendedattr.py b/test/orm/extendedattr.py index eba9a94223..5f224955e7 100644 --- a/test/orm/extendedattr.py +++ b/test/orm/extendedattr.py @@ -315,5 +315,20 @@ class ReconstituteTest(testing.TestBase): s._run_on_load(m) assert recon == ['go'] + def test_inheritance(self): + recon = [] + class MyBaseClass(object): + @attributes.on_reconstitute + def recon(self): + recon.append('go') + + class MySubClass(MyBaseClass): + pass + attributes.register_class(MySubClass) + m = attributes.manager_of_class(MySubClass).new_instance() + s = attributes.instance_state(m) + s._run_on_load(m) + assert recon == ['go'] + if __name__ == '__main__': testing.main()