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
>> <[email protected]> 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
>> >> <[email protected]> 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 [email protected].
>> >> > To post to this group, send email to [email protected].
>> >> > 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 [email protected].
>> > To post to this group, send email to [email protected].
>> > 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 [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.