I'm replying to my original post since it's related. I'm still seeing missing synonyms on child classes when they are defined in a mixin imported on a parent.
import sqlalchemy as sa from sqlalchemy import orm from sqlalchemy.ext.declarative import declarative_base, declared_attr Base = declarative_base() class DirectoryEntry(Base): guid = sa.Column(sa.Integer, primary_key=True) _type = sa.Column(sa.String, nullable=False) distinguished_name = sa.Column(sa.String) name = sa.Column(sa.String) __tablename__ = 'directory_entry' __mapper_args__ = { 'polymorphic_on': _type, 'polymorphic_identity': 'directory_entry', } class DirectoryGroup(DirectoryEntry): __mapper_args__ = { 'polymorphic_identity': 'directory_group', } class ActiveDirectoryEntry: @declared_attr def distinguishedName(self): return orm.synonym('distinguished_name') class ActiveDirectoryGroup(ActiveDirectoryEntry, DirectoryGroup): __mapper_args__ = { 'polymorphic_identity': 'active_directory_group' } class AnotherChild(ActiveDirectoryGroup): __mapper_args__ = { 'polymorphic_identity': 'another_child' } engine = sa.create_engine('postgresql+psycopg2://postgres@localhost/postgres', echo=True, isolation_level='AUTOCOMMIT') engine.execute('DROP DATABASE IF EXISTS inherit_test') engine.execute('CREATE DATABASE inherit_test') engine = sa.create_engine('postgresql+psycopg2://postgres@localhost/inherit_test', echo=True) Base.metadata.create_all(engine) session = orm.sessionmaker(bind=engine)() group = ActiveDirectoryGroup( name='Users', distinguishedName='cn=Users,ou=domain', ) child = AnotherChild( name='Admins', distinguishedName='cn=Admins,ou=domain', ) session.add(group) session.add(child) session.flush() session.commit() group = session.query(ActiveDirectoryGroup).filter(ActiveDirectoryGroup.name == 'Users').one() group_mapper = sa.inspect(group.__class__) group_synonyms = group_mapper.synonyms.keys() child = session.query(AnotherChild).filter(AnotherChild.name == 'Admins').one() child_mapper = sa.inspect(child.__class__) child_synonyms = child_mapper.synonyms.keys() assert child_synonyms == group_synonyms Maybe I'm off in unsupported land again? I could define AnotherChild identical to ActiveDirectoryGroup, but there are additional synonyms on ActiveDirectoryGroup they both should have. Thanks, Derek On Wednesday, May 9, 2018 at 2:16:22 PM UTC-5, Derek Lambert wrote: > > That was my conclusion too after consulting the googles. > > I've done as you suggested and things are working as expected. Thanks! > > On Monday, April 30, 2018 at 4:26:02 PM UTC-5, Mike Bayer wrote: >> >> On Mon, Apr 30, 2018 at 4:18 PM, Derek Lambert >> <dlam...@dereklambert.com> wrote: >> >> >> >> mmm what do you mean by "mixin" here, it looks like every class you >> >> have is mapped. >> >> >> > >> > They are mapped in the code, but that's only so I can query them. I >> > attempted to make LdapEntry and ActiveDirectoryEntry true mixin's by >> setting >> > __abstract__ = True. >> > >> >> >> >> >> >> this a heavy set of inheritance and I might also use composition >> >> instead, though that would change your DB design. >> >> >> > >> > The design isn't in production yet so now would be the time to change >> it. >> > Are you aware of any SQLAlchemy projects using composition I could >> review? >> >> mmm not specifically, it means you might do something like store >> "Entry" concepts in one table and "User" concepts in another. >> looking more closely this seems like it would be akward also. >> >> looking more closely at your mappings it looks like only >> DirectoryEntry and DirectoryUser actually have any columns. The rest >> is all synonyms. I'd likely use mixins for all those synonym sets. >> >> >> > >> > Thanks, >> > Derek >> > >> > On Monday, April 30, 2018 at 1:30:51 PM UTC-5, Mike Bayer wrote: >> >> >> >> On Mon, Apr 30, 2018 at 1:33 PM, Derek Lambert >> >> <dlam...@dereklambert.com> wrote: >> >> > I'm running into an issue in a hierarchy of single-table inheritance >> >> > objects >> >> > with multiple inheritance. The objects represent users/groups/etc. >> from >> >> > various directories and applications. >> >> > >> >> > Retrieving the list of synonyms from an object at the bottom of the >> >> > inheritance tree doesn't return the entire list of synonyms. >> >> > >> >> > When I make some of the "mixin" type objects abstract the synonyms >> >> > returned >> >> > are as expected, but I lose the ability to query those objects. >> >> >> >> mmm what do you mean by "mixin" here, it looks like every class you >> >> have is mapped. >> >> >> >> I will say that what you are doing here: >> >> >> >> class LdapUser(DirectoryUser, LdapEntry): >> >> givenName = orm.synonym('first_name') >> >> sn = orm.synonym('last_name') >> >> __mapper_args__ = { >> >> 'polymorphic_identity': 'ldap_user', >> >> } >> >> >> >> where DirectoryUser and LdapEntry are also both mapped, I'm amazed >> >> that even works. That's not at all anything that has ever been >> >> supported or attempted, as each mapper only "inherits" from at most >> >> one mapped class - while declarative supports actual "mixin" classes, >> >> where by "mixin" we mean "non-mapped class", nothing in SQLAlchemy ORM >> >> is expecting multiple inheritance at the mapper level. Above, I >> >> guess it's picking one superclass mapper at random to be "inherits", >> >> an ignoring the other, and that is likely the source of your issue. >> >> Unfortunately I think you have to work out this hierarchy in terms of >> >> single-inhertanace for classes that are actually mapped, which means >> >> adding some non-mapped "mixin" classes that just accommodate for the >> >> extra synonyms, something like: >> >> >> >> class DirectoryEntry(Base): >> >> >> >> class AbstractDirectoryUser(object): >> >> # synonyms >> >> >> >> class DirectoryUser(AbstractDirectoryUser, DirectoryEntry): >> >> >> >> class LdapEntry(DirectoryEntry): >> >> >> >> class LdapUser(AbstractDirectoryUser, LdapEntry): >> >> >> >> this a heavy set of inheritance and I might also use composition >> >> instead, though that would change your DB design. >> >> >> >> >> >> > >> >> > Maybe I'm overlooking a simpler implementation, or simply using >> >> > SQLAlchemy >> >> > in a way that wasn't intended? >> >> > >> >> > Here's a simplified subset of the code. In practice any object >> ending >> >> > with >> >> > Entry and the base DirectoryUser and DirectoryGroup wouldn't be >> created. >> >> > >> >> > import sqlalchemy as sa >> >> > import sqlalchemy.orm as orm >> >> > from sqlalchemy.ext.declarative import declarative_base >> >> > >> >> > >> >> > Base = declarative_base() >> >> > >> >> > >> >> > class DirectoryEntry(Base): >> >> > guid = sa.Column(sa.Integer, primary_key=True) >> >> > _type = sa.Column(sa.String, nullable=False, >> >> > index=True) >> >> > distinguished_name = sa.Column(sa.String, index=True) >> >> > name = sa.Column(sa.String, index=True) >> >> > >> >> > __tablename__ = 'directory_entry' >> >> > __mapper_args__ = { >> >> > 'polymorphic_on': _type, >> >> > 'polymorphic_identity': 'directory_entry', >> >> > } >> >> > >> >> > >> >> > class DirectoryUser(DirectoryEntry): >> >> > first_name = sa.Column(sa.String) >> >> > last_name = sa.Column(sa.String) >> >> > email = sa.Column(sa.String) >> >> > username = sa.Column(sa.String) >> >> > >> >> > __mapper_args__ = { >> >> > 'polymorphic_identity': 'directory_user', >> >> > } >> >> > >> >> > >> >> > class LdapEntry(DirectoryEntry): >> >> > cn = orm.synonym('name') >> >> > >> >> > __mapper_args__ = { >> >> > 'polymorphic_identity': 'ldap_entry', >> >> > } >> >> > >> >> > >> >> > class LdapUser(DirectoryUser, LdapEntry): >> >> > givenName = orm.synonym('first_name') >> >> > sn = orm.synonym('last_name') >> >> > >> >> > __mapper_args__ = { >> >> > 'polymorphic_identity': 'ldap_user', >> >> > } >> >> > >> >> > >> >> > class ActiveDirectoryEntry(LdapEntry): >> >> > distinguishedName = orm.synonym('distinguished_name') >> >> > >> >> > __mapper_args__ = { >> >> > 'polymorphic_identity': 'active_directory_entry', >> >> > } >> >> > >> >> > >> >> > class ActiveDirectoryUser(LdapUser, ActiveDirectoryEntry): >> >> > mail = orm.synonym('email') >> >> > sAMAccountName = orm.synonym('username') >> >> > >> >> > __mapper_args__ = { >> >> > 'polymorphic_identity': 'active_directory_user' >> >> > } >> >> > >> >> > >> >> > engine_url = 'postgresql+psycopg2://postgres@localhost/inherit_test' >> >> > engine = sa.create_engine(engine_url, echo=True) >> >> > >> >> > Base.metadata.create_all(engine) >> >> > >> >> > session = orm.sessionmaker(bind=engine)() >> >> > ad_user = ActiveDirectoryUser( >> >> > cn='John Doe', >> >> > sAMAccountName='jdoe', >> >> > distinguishedName='ou=domain', >> >> > givenName='John' >> >> > ) >> >> > >> >> > session.add(ad_user) >> >> > session.commit() >> >> > >> >> > user1 = session.query(DirectoryUser).filter(DirectoryUser.username >> == >> >> > 'jdoe').one() >> >> > user3 = session.query(LdapUser).filter(LdapUser.username == >> >> > 'jdoe').one() >> >> > user2 = >> >> > >> session.query(ActiveDirectoryUser).filter(ActiveDirectoryUser.username >> >> > == >> >> > 'jdoe').one() >> >> > user4 = session.query(DirectoryEntry).filter(DirectoryEntry.name == >> >> > 'John >> >> > Doe').one() >> >> > >> >> > assert(user1 == user2 == user3 == user4) >> >> > >> >> > mapper = sa.inspect(ad_user.__class__) >> >> > synonyms = mapper.synonyms.keys() >> >> > >> >> > assert(synonyms == ['mail', 'sAMAccountName', 'givenName', 'sn', >> 'cn', >> >> > 'distinguishedName']) >> >> > >> >> > >> >> > Any help is appreciated! >> >> > >> >> > -- >> >> > SQLAlchemy - >> >> > The Python SQL Toolkit and Object Relational Mapper >> >> > >> >> > http://www.sqlalchemy.org/ >> >> > >> >> > To post example code, please provide an MCVE: Minimal, Complete, and >> >> > Verifiable Example. See http://stackoverflow.com/help/mcve for a >> full >> >> > description. >> >> > --- >> >> > You received this message because you are subscribed to the Google >> >> > Groups >> >> > "sqlalchemy" group. >> >> > To unsubscribe from this group and stop receiving emails from it, >> send >> >> > an >> >> > email to sqlalchemy+...@googlegroups.com. >> >> > To post to this group, send email to sqlal...@googlegroups.com. >> >> > Visit this group at https://groups.google.com/group/sqlalchemy. >> >> > For more options, visit https://groups.google.com/d/optout. >> > >> > -- >> > SQLAlchemy - >> > The Python SQL Toolkit and Object Relational Mapper >> > >> > http://www.sqlalchemy.org/ >> > >> > To post example code, please provide an MCVE: Minimal, Complete, and >> > Verifiable Example. See http://stackoverflow.com/help/mcve for a full >> > description. >> > --- >> > You received this message because you are subscribed to the Google >> Groups >> > "sqlalchemy" group. >> > To unsubscribe from this group and stop receiving emails from it, send >> an >> > email to sqlalchemy+...@googlegroups.com. >> > To post to this group, send email to sqlal...@googlegroups.com. >> > Visit this group at https://groups.google.com/group/sqlalchemy. >> > For more options, visit https://groups.google.com/d/optout. >> > -- SQLAlchemy - The Python SQL Toolkit and Object Relational Mapper http://www.sqlalchemy.org/ To post example code, please provide an MCVE: Minimal, Complete, and Verifiable Example. See http://stackoverflow.com/help/mcve for a full description. --- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To unsubscribe from this group and stop receiving emails from it, send an email to sqlalchemy+unsubscr...@googlegroups.com. To post to this group, send email to sqlalchemy@googlegroups.com. Visit this group at https://groups.google.com/group/sqlalchemy. For more options, visit https://groups.google.com/d/optout.