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 <javascript:>> 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 <javascript:>. > > To post to this group, send email to sqlal...@googlegroups.com > <javascript:>. > > 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.