From 9335c24d6c98033f4aa1ceafd23a70b88c8ae811 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Fri, 19 Oct 2018 17:10:42 -0400 Subject: [PATCH] Check more specifically for hybrid attr and not mapped property Fixed regression caused by :ticket:`4326` in version 1.2.12 where using :class:`.declared_attr` with a mixin in conjunction with :func:`.orm.synonym` would fail to map the synonym properly to an inherited subclass. Fixes: #4350 Change-Id: Ib2a9b6a125a2ac7c7ff80201746b7f10e5596226 --- doc/build/changelog/unreleased_12/4350.rst | 8 ++++ lib/sqlalchemy/ext/declarative/base.py | 3 +- test/ext/declarative/test_mixin.py | 53 +++++++++++++++++++++- 3 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 doc/build/changelog/unreleased_12/4350.rst diff --git a/doc/build/changelog/unreleased_12/4350.rst b/doc/build/changelog/unreleased_12/4350.rst new file mode 100644 index 0000000000..d3216a5bc4 --- /dev/null +++ b/doc/build/changelog/unreleased_12/4350.rst @@ -0,0 +1,8 @@ +.. change:: + :tags: bug, orm, declarative + :tickets: 4350 + + Fixed regression caused by :ticket:`4326` in version 1.2.12 where using + :class:`.declared_attr` with a mixin in conjunction with + :func:`.orm.synonym` would fail to map the synonym properly to an inherited + subclass. diff --git a/lib/sqlalchemy/ext/declarative/base.py b/lib/sqlalchemy/ext/declarative/base.py index 9e15582d6f..a6642364dc 100644 --- a/lib/sqlalchemy/ext/declarative/base.py +++ b/lib/sqlalchemy/ext/declarative/base.py @@ -295,7 +295,8 @@ class _MapperConfig(object): # produces nested proxies, so we are only # looking one level deep right now. if isinstance(ret, InspectionAttr) and \ - ret._is_internal_proxy: + ret._is_internal_proxy and not isinstance( + ret.original_property, MapperProperty): ret = ret.descriptor dict_[name] = column_copies[obj] = ret diff --git a/test/ext/declarative/test_mixin.py b/test/ext/declarative/test_mixin.py index 6f95bed60f..fa8c36656c 100644 --- a/test/ext/declarative/test_mixin.py +++ b/test/ext/declarative/test_mixin.py @@ -7,7 +7,7 @@ from sqlalchemy import Integer, String, ForeignKey, select, func from sqlalchemy.testing.schema import Table, Column from sqlalchemy.orm import relationship, create_session, class_mapper, \ configure_mappers, clear_mappers, \ - deferred, column_property, Session, base as orm_base + deferred, column_property, Session, base as orm_base, synonym from sqlalchemy.util import classproperty from sqlalchemy.ext.declarative import declared_attr, declarative_base from sqlalchemy.orm import events as orm_events @@ -1252,7 +1252,9 @@ class DeclarativeMixinTest(DeclarativeTestBase): assert isinstance(SomeAbstract.__dict__['some_attr'], declared_attr) -class DeclarativeMixinPropertyTest(DeclarativeTestBase): +class DeclarativeMixinPropertyTest( + DeclarativeTestBase, + testing.AssertsCompiledSQL): def test_column_property(self): @@ -1371,6 +1373,53 @@ class DeclarativeMixinPropertyTest(DeclarativeTestBase): d1.all_orm_descriptors['hp2'], ) + def test_correct_for_proxies_doesnt_impact_synonyms(self): + from sqlalchemy import inspect + + class Mixin(object): + @declared_attr + def data_syn(cls): + return synonym('data') + + class Base(declarative_base(), Mixin): + __tablename__ = 'test' + id = Column(String, primary_key=True) + data = Column(String) + type = Column(String) + __mapper_args__ = { + 'polymorphic_on': type, + 'polymorphic_identity': 'base' + } + + class Derived(Base): + __mapper_args__ = { + 'polymorphic_identity': 'derived' + } + + assert Base.data_syn._is_internal_proxy + assert Derived.data_syn._is_internal_proxy + + b1 = inspect(Base) + d1 = inspect(Derived) + is_( + b1.attrs['data_syn'], + d1.attrs['data_syn'], + ) + + s = Session() + self.assert_compile( + s.query(Base.data_syn).filter(Base.data_syn == 'foo'), + 'SELECT test.data AS test_data FROM test WHERE test.data = :data_1', + dialect='default' + ) + self.assert_compile( + s.query(Derived.data_syn).filter(Derived.data_syn == 'foo'), + 'SELECT test.data AS test_data FROM test WHERE test.data = ' + ':data_1 AND test.type IN (:type_1)', + dialect='default', + checkparams={"type_1": "derived", "data_1": "foo"} + ) + def test_column_in_mapper_args(self): class MyMixin(object): -- 2.47.2