>
>
> 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?

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 <javascript:>> 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 <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.

Reply via email to