<p dir="auto"><strong>Migrated issue, originally created by Derek Lambert (<a href="https://github.com/dlambert">@dlambert</a>)</strong></p>
<p dir="auto">Appologies if this explanation isn't accurate, the code is. After <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="384640437" data-permission-text="Title is private" data-url="https://github.com/sqlalchemy/sqlalchemy/issues/4116" data-hovercard-type="issue" data-hovercard-url="/sqlalchemy/sqlalchemy/issues/4116/hovercard" href="https://github.com/sqlalchemy/sqlalchemy/issues/4116">#4116</a>, an association proxy specifing a property defined in a subclass no longer resolves properly.</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="import sqlalchemy as sa
import sqlalchemy.orm as orm
from sqlalchemy.dialects.postgresql import UUID as saUUID
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.ext.declarative import declarative_base
from uuid import uuid4, UUID

Base = declarative_base()


class DirectoryEntry(Base):
    guid = sa.Column(saUUID, primary_key=True)
    directory_name = sa.Column(sa.String, default='dummy_fk', primary_key=True)
    _type = sa.Column(sa.String, nullable=False)
    distinguished_name = sa.Column(sa.String)
    name = sa.Column(sa.String)
    member_of = association_proxy('_groups', 'group')

    __tablename__ = 'directory_entry'
    __table_args__ = (
        sa.UniqueConstraint('directory_name', 'guid'),
        sa.UniqueConstraint('directory_name', 'distinguished_name'),
    )
    __mapper_args__ = {
        'polymorphic_on':   _type,
        'with_polymorphic': '*'
    }


class DirectoryUser(DirectoryEntry):
    username = sa.Column(sa.String)


class DirectoryGroup(DirectoryEntry):
    pass


class DirectoryGroupMember(Base):
    guid = sa.Column(saUUID, primary_key=True, default=lambda: str(uuid4()))
    directory_name = sa.Column(sa.String, primary_key=True)
    distinguished_name = sa.Column(sa.String)
    group_guid = sa.Column(saUUID)
    member_guid = sa.Column(saUUID)

    __tablename__ = 'directory_group_member'
    __table_args__ = (
        sa.UniqueConstraint('group_guid', 'distinguished_name'),
        sa.ForeignKeyConstraint(('group_guid', 'directory_name'), ('directory_entry.guid', 'directory_entry.directory_name')),
    )


class VaultEntry(DirectoryEntry):
    pass


class VaultUser(DirectoryUser, VaultEntry):
    _groups = orm.relationship(&quot;VaultGroupMember&quot;, primaryjoin=&quot;and_(foreign(VaultGroupMember.distinguished_name)==VaultUser.username)&quot;)

    __mapper_args__ = {'polymorphic_identity': 'vault_user'}


class VaultGroup(DirectoryGroup, VaultEntry):
    _groups = orm.relationship('VaultGroupMember', primaryjoin='and_(foreign(VaultGroupMember.distinguished_name)==VaultGroup.name)')
    _members = orm.relationship('VaultGroupMember', backref='group')
    members = association_proxy('_members', 'member', creator=lambda v: VaultGroupMember(distinguished_name=v))

    __mapper_args__ = {'polymorphic_identity': 'vault_group'}


class VaultGroupMember(DirectoryGroupMember):
    @property
    def member(self):
        return orm.object_session(self).query(DirectoryEntry).filter(sa.or_(DirectoryGroup.name == self.distinguished_name, DirectoryUser.username == self.distinguished_name)).one()


engine_url = 'postgresql+psycopg2://postgres@localhost/bad_proxy'
engine = sa.create_engine(engine_url, echo=True)
Base.metadata.create_all(engine)
Session = orm.sessionmaker(bind=engine)
sess = Session()

user1 = {
    'guid':     str(UUID(int=1)),
    'name':     'Jon Doe',
    'username': 'jon.doe'
}
group1 = {
    'guid':    str(UUID(int=101)),
    'name':    'Group1',
    'members': ['jon.doe']
}

sess.add(VaultUser(**user1))
sess.add(VaultGroup(**group1))
sess.commit()
sess.flush()

user = sess.query(DirectoryUser).filter(DirectoryUser.directory_name == 'dummy_fk').filter(DirectoryUser.username == 'jon.doe').one_or_none()
groups = list(user.member_of)
assert len(groups)
"><pre class="notranslate"><code class="notranslate">import sqlalchemy as sa
import sqlalchemy.orm as orm
from sqlalchemy.dialects.postgresql import UUID as saUUID
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.ext.declarative import declarative_base
from uuid import uuid4, UUID

Base = declarative_base()


class DirectoryEntry(Base):
    guid = sa.Column(saUUID, primary_key=True)
    directory_name = sa.Column(sa.String, default='dummy_fk', primary_key=True)
    _type = sa.Column(sa.String, nullable=False)
    distinguished_name = sa.Column(sa.String)
    name = sa.Column(sa.String)
    member_of = association_proxy('_groups', 'group')

    __tablename__ = 'directory_entry'
    __table_args__ = (
        sa.UniqueConstraint('directory_name', 'guid'),
        sa.UniqueConstraint('directory_name', 'distinguished_name'),
    )
    __mapper_args__ = {
        'polymorphic_on':   _type,
        'with_polymorphic': '*'
    }


class DirectoryUser(DirectoryEntry):
    username = sa.Column(sa.String)


class DirectoryGroup(DirectoryEntry):
    pass


class DirectoryGroupMember(Base):
    guid = sa.Column(saUUID, primary_key=True, default=lambda: str(uuid4()))
    directory_name = sa.Column(sa.String, primary_key=True)
    distinguished_name = sa.Column(sa.String)
    group_guid = sa.Column(saUUID)
    member_guid = sa.Column(saUUID)

    __tablename__ = 'directory_group_member'
    __table_args__ = (
        sa.UniqueConstraint('group_guid', 'distinguished_name'),
        sa.ForeignKeyConstraint(('group_guid', 'directory_name'), ('directory_entry.guid', 'directory_entry.directory_name')),
    )


class VaultEntry(DirectoryEntry):
    pass


class VaultUser(DirectoryUser, VaultEntry):
    _groups = orm.relationship("VaultGroupMember", primaryjoin="and_(foreign(VaultGroupMember.distinguished_name)==VaultUser.username)")

    __mapper_args__ = {'polymorphic_identity': 'vault_user'}


class VaultGroup(DirectoryGroup, VaultEntry):
    _groups = orm.relationship('VaultGroupMember', primaryjoin='and_(foreign(VaultGroupMember.distinguished_name)==VaultGroup.name)')
    _members = orm.relationship('VaultGroupMember', backref='group')
    members = association_proxy('_members', 'member', creator=lambda v: VaultGroupMember(distinguished_name=v))

    __mapper_args__ = {'polymorphic_identity': 'vault_group'}


class VaultGroupMember(DirectoryGroupMember):
    @property
    def member(self):
        return orm.object_session(self).query(DirectoryEntry).filter(sa.or_(DirectoryGroup.name == self.distinguished_name, DirectoryUser.username == self.distinguished_name)).one()


engine_url = 'postgresql+psycopg2://postgres@localhost/bad_proxy'
engine = sa.create_engine(engine_url, echo=True)
Base.metadata.create_all(engine)
Session = orm.sessionmaker(bind=engine)
sess = Session()

user1 = {
    'guid':     str(UUID(int=1)),
    'name':     'Jon Doe',
    'username': 'jon.doe'
}
group1 = {
    'guid':    str(UUID(int=101)),
    'name':    'Group1',
    'members': ['jon.doe']
}

sess.add(VaultUser(**user1))
sess.add(VaultGroup(**group1))
sess.commit()
sess.flush()

user = sess.query(DirectoryUser).filter(DirectoryUser.directory_name == 'dummy_fk').filter(DirectoryUser.username == 'jon.doe').one_or_none()
groups = list(user.member_of)
assert len(groups)

</code></pre></div>
<p dir="auto">This works in 1.2.2, with 1.2.3 raises exception:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="Traceback (most recent call last):
  File &quot;/Users/dereklambert/Library/Python/3.6/lib/python/site-packages/sqlalchemy/orm/mapper.py&quot;, line 1922, in get_property
    return self._props[key]
KeyError: '_groups'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File &quot;bad_proxy2.py&quot;, line 100, in &lt;module&gt;
    groups = list(user.member_of)
  File &quot;/Users/dereklambert/Library/Python/3.6/lib/python/site-packages/sqlalchemy/ext/associationproxy.py&quot;, line 283, in __get__
    if self.scalar:
  File &quot;/Users/dereklambert/Library/Python/3.6/lib/python/site-packages/sqlalchemy/util/langhelpers.py&quot;, line 767, in __get__
    obj.__dict__[self.__name__] = result = self.fget(obj)
  File &quot;/Users/dereklambert/Library/Python/3.6/lib/python/site-packages/sqlalchemy/ext/associationproxy.py&quot;, line 238, in scalar
    scalar = not self._get_property().uselist
  File &quot;/Users/dereklambert/Library/Python/3.6/lib/python/site-packages/sqlalchemy/ext/associationproxy.py&quot;, line 221, in _get_property
    get_property(self.target_collection))
  File &quot;/Users/dereklambert/Library/Python/3.6/lib/python/site-packages/sqlalchemy/orm/mapper.py&quot;, line 1925, in get_property
    &quot;Mapper '%s' has no property '%s'&quot; % (self, key))
sqlalchemy.exc.InvalidRequestError: Mapper 'Mapper|DirectoryEntry|directory_entry' has no property '_groups'
"><pre class="notranslate"><code class="notranslate">Traceback (most recent call last):
  File "/Users/dereklambert/Library/Python/3.6/lib/python/site-packages/sqlalchemy/orm/mapper.py", line 1922, in get_property
    return self._props[key]
KeyError: '_groups'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "bad_proxy2.py", line 100, in &lt;module&gt;
    groups = list(user.member_of)
  File "/Users/dereklambert/Library/Python/3.6/lib/python/site-packages/sqlalchemy/ext/associationproxy.py", line 283, in __get__
    if self.scalar:
  File "/Users/dereklambert/Library/Python/3.6/lib/python/site-packages/sqlalchemy/util/langhelpers.py", line 767, in __get__
    obj.__dict__[self.__name__] = result = self.fget(obj)
  File "/Users/dereklambert/Library/Python/3.6/lib/python/site-packages/sqlalchemy/ext/associationproxy.py", line 238, in scalar
    scalar = not self._get_property().uselist
  File "/Users/dereklambert/Library/Python/3.6/lib/python/site-packages/sqlalchemy/ext/associationproxy.py", line 221, in _get_property
    get_property(self.target_collection))
  File "/Users/dereklambert/Library/Python/3.6/lib/python/site-packages/sqlalchemy/orm/mapper.py", line 1925, in get_property
    "Mapper '%s' has no property '%s'" % (self, key))
sqlalchemy.exc.InvalidRequestError: Mapper 'Mapper|DirectoryEntry|directory_entry' has no property '_groups'

</code></pre></div>
<p dir="auto">Python 3.6.4<br>
PostgreSQL 10<br>
psycopg2 2.7.3.2</p>
<p dir="auto"><strong>Migrated issue, originally created by Brendan Abel (<a href="https://github.com/babel">@babel</a>)</strong></p>
<p dir="auto">This is using sqlalchemy 0.9.8</p>
<p dir="auto">Assuming a declarative class called "MyClass" with an association proxy field call "my_field"</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="&gt;&gt;&gt; ap = MyClass.__mapper__.all_orm_descriptors['my_field']
&gt;&gt;&gt; ap.owning_class
None

&gt;&gt;&gt; ap = MyClass.my_field
&gt;&gt;&gt; ap.owning_class
&lt;class 'MyClass'&gt;

&gt;&gt;&gt; ap = MyClass.__mapper__.all_orm_descriptors['my_field']
&gt;&gt;&gt; ap.owning_class
&lt;class 'MyClass'&gt;"><pre class="notranslate"><code class="notranslate">&gt;&gt;&gt; ap = MyClass.__mapper__.all_orm_descriptors['my_field']
&gt;&gt;&gt; ap.owning_class
None

&gt;&gt;&gt; ap = MyClass.my_field
&gt;&gt;&gt; ap.owning_class
&lt;class 'MyClass'&gt;

&gt;&gt;&gt; ap = MyClass.__mapper__.all_orm_descriptors['my_field']
&gt;&gt;&gt; ap.owning_class
&lt;class 'MyClass'&gt;
</code></pre></div>
<p dir="auto">Without the owning_class set, most of the other properties will raise exceptions when trying to access them.  This can be problematic when using the mapper class to procedurally access fields via the mapper.</p>
<hr>
<p dir="auto">Attachments: <a href="../wiki/imported_issue_attachments/3423/rework_attribute_proxy_owner.patch">rework_attribute_proxy_owner.patch</a> | <a href="../wiki/imported_issue_attachments/3423/aptest.py">aptest.py</a></p>