On Wed, 16 Nov 2016 11:38:20 -0500
mike bayer <[email protected]> wrote:
>
>
> On 11/16/2016 11:29 AM, Michael Williamson wrote:
> >> I have no idea what you are trying to accomplish. Why not use
> >> joined inheritance normally? When you query for Employee, you will
> >> get Engineer / Manager objects back, they will have .name.
> >>
> >> If you're trying to make it so that only one SELECT is emitted in
> >> order to get fully populated Engineer/Manager, joined inheritance
> >> already does that as well, use plain with_polymorphic for that, the
> >> SQL is constructed automatically.
> >
> > Sorry, I haven't done a good job of explaining my use-case very
> > well. I want to be able to treat the name from both Engineer and
> > Manager as the same thing on Employee, so that I can write queries
> > like:
> >
> > session.query(Employee.name)
> >
> > and also:
> >
> > session.query(Employee)
>
> So you need Employee to be with_polymorphic in all cases and you need
> a .name attribute that is a column property of the coalesce of all
> such name fields. See below.
>
> from sqlalchemy import *
> from sqlalchemy.orm import *
> from sqlalchemy.ext.declarative import declarative_base
>
> Base = declarative_base()
>
>
> class Employee(Base):
> __tablename__ = 'employee'
> id = Column(Integer, primary_key=True)
> name = Column(String(50))
> type = Column(String(50))
>
> __mapper_args__ = {
> 'polymorphic_identity': 'employee',
> 'polymorphic_on': type,
> 'with_polymorphic': '*'
> }
>
>
> class Engineer(Employee):
> __tablename__ = 'engineer'
> id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
> engineer_name = Column(String(30))
>
> __mapper_args__ = {
> 'polymorphic_identity': 'engineer',
> }
>
>
> class Manager(Employee):
> __tablename__ = 'manager'
> id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
> manager_name = Column(String(30))
>
> __mapper_args__ = {
> 'polymorphic_identity': 'manager',
> }
>
>
> Employee.name = func.coalesce(Engineer.engineer_name,
> Manager.manager_name)
>
>
> e = create_engine("sqlite://", echo=True)
> Base.metadata.create_all(e)
>
> s = Session(e)
> s.add_all([
> Engineer(engineer_name='e1'),
> Engineer(engineer_name='e2'),
> Manager(manager_name='m1'),
> ])
>
> s.commit()
>
> print s.query(Employee).filter(Employee.name.in_(['e2', 'm1'])).all()
>
> print s.query(Employee.name).select_from(Employee).all()
>
>
> query output is:
>
> SELECT employee.id AS employee_id, employee.name AS employee_name,
> employee.type AS employee_type, engineer.id AS engineer_id,
> engineer.engineer_name AS engineer_engineer_name, manager.id AS
> manager_id, manager.manager_name AS manager_manager_name
> FROM employee LEFT OUTER JOIN engineer ON employee.id = engineer.id
> LEFT OUTER JOIN manager ON employee.id = manager.id
> WHERE coalesce(engineer.engineer_name, manager.manager_name) IN (?, ?)
> 2016-11-16 11:36:26,868 INFO sqlalchemy.engine.base.Engine ('e2',
> 'm1') [<__main__.Engineer object at 0x7f4dd4407c50>,
> <__main__.Manager object at 0x7f4dd4407e10>]
>
> 2016-11-16 11:37:30,029 INFO sqlalchemy.engine.base.Engine SELECT
> coalesce(engineer.engineer_name, manager.manager_name) AS coalesce_1
> FROM employee LEFT OUTER JOIN engineer ON employee.id = engineer.id
> LEFT OUTER JOIN manager ON employee.id = manager.id
> 2016-11-16 11:37:30,029 INFO sqlalchemy.engine.base.Engine ()
> [(u'e1',), (u'e2',), (u'm1',)]
>
Thanks, that seems to get most of the way there. The only problem is
that calling .name on the instance returns None:
print s.query(Employee).select_from(Employee).first().name
It seems like getting rid of Employee.name and renaming manager_name
and engineer_name to just name works -- is there a reason that's a bad
idea?
>
>
>
>
>
>
>
> >
> > Joined inheritance lets you do that if you put the name on the
> > employee, but I'd like to keep the name on the individual subtypes.
> > For my actual use case, some of the columns on one of the subtypes
> > is calculated from other bits of SQL: in other words, the way the
> > columns are written out in SQL needs to differ for each type, but
> > I'd like to be able to write queries to select the columns without
> > having to explicitly write out the union each time.
> >
> > So for better motivation, imagine if Engineer.name was defined as:
> >
> > @hybrid_property
> > def name(self):
> > return "E" + str(self.id)
> >
> > @name.expression
> > def name(cls):
> > return ("E" + cls.id.cast(String())).label("name")
> >
> > while Manager.name remains an ordinary column.
> >
> > --
> > 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]
> > <mailto:[email protected]>.
> > To post to this group, send email to [email protected]
> > <mailto:[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.