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 <mike...@zzzcomputing.com> wrote:
>
> On Fri, Oct 19, 2018 at 2:30 PM Derek Lambert <dlamb...@dereklambert.com> 
> 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+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.

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