Thanks. I'll use cascading as a workaround for the time being.

On Friday, October 19, 2018 at 3:55:37 PM UTC-5, Mike Bayer wrote:
>
> it's a recent regression as of 1.2.12: 
>
> https://bitbucket.org/zzzeek/sqlalchemy/issues/4350/synonym-single-inheritance-mixins
>  
>
> will fix 
>
> On Fri, Oct 19, 2018 at 4:38 PM Mike Bayer <mik...@zzzcomputing.com 
> <javascript:>> wrote: 
> > 
> > On Fri, Oct 19, 2018 at 2:30 PM Derek Lambert <dlam...@dereklambert.com 
> <javascript:>> wrote: 
> > > 
> > > 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. 
> > 
> > the synonym() is only applied to ActiveDirectoryGroup because it is 
> > naturally inherited by AnotherChild: 
> > 
> > child_synonyms = child_mapper.inherits.synonyms.keys() 
> > 
> > assert child_synonyms == group_synonyms 
> > 
> > but it's not getting mapped anyway, which seems like a bug.  works 
> > with @cascading though: 
> > 
> > class ActiveDirectoryEntry: 
> >     @declared_attr.cascading 
> >     def distinguishedName(cls): 
> >         return orm.synonym('distinguished_name') 
> > 
> > 
> > c1 = session.query(AnotherChild.distinguishedName).filter( 
> >     AnotherChild.distinguishedName == "cn=Admins,ou=domain").scalar() 
> > g1 = session.query(ActiveDirectoryGroup.distinguishedName).filter( 
> >     ActiveDirectoryGroup.distinguishedName == 
> "cn=Users,ou=domain").scalar() 
> > 
> > print(g1) 
> > print(c1) 
> > 
> > 
> > 
> > 
> > > 
> > > 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+...@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