Issue 1:
Migrated issue, originally created by Derek Lambert (@dlambert)

Appologies if this explanation isn't accurate, the code is. After #4116, an association proxy specifing a property defined in a subclass no longer resolves properly.

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)

This works in 1.2.2, with 1.2.3 raises exception:

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 <module>
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'

Python 3.6.4

PostgreSQL 10

psycopg2 2.7.3.2

Issue 2:
Migrated issue, originally created by Brendan Abel (@babel)

This is using sqlalchemy 0.9.8

Assuming a declarative class called "MyClass" with an association proxy field call "my_field"

>>> ap = MyClass.__mapper__.all_orm_descriptors['my_field']
>>> ap.owning_class
None

>>> ap = MyClass.my_field
>>> ap.owning_class
<class 'MyClass'>

>>> ap = MyClass.__mapper__.all_orm_descriptors['my_field']
>>> ap.owning_class
<class 'MyClass'>

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.

Attachments: rework_attribute_proxy_owner.patch | aptest.py