On Sat, Mar 9, 2019 at 10:12 PM <jay.c...@freenome.com> wrote:
>
> Ah my bad! I really didn't do good testing. Your reply prompted me to find 
> the root of the problem and solution though. Posting it here just in case 
> this is useful to anyone else!
>
> A gist of the problem I actually had was that I has a column_property in 
> Common defined in terms of the Superclass, but really needed to be defined on 
> the polymorphic alias of the superclass instead. In full:
>
> from sqlalchemy import Column
> from sqlalchemy import create_engine
> from sqlalchemy import ForeignKey
> from sqlalchemy import Integer
> from sqlalchemy import String
> from sqlalchemy.ext.declarative import declarative_base
> from sqlalchemy.orm import contains_eager
> from sqlalchemy.orm import relationship
> from sqlalchemy.orm import Session
> from sqlalchemy.orm import with_polymorphic
> from sqlalchemy.orm import column_property
> from sqlalchemy import select, func, and_, event
>
> Base = declarative_base()
>
>
> class Superclass(Base):
>     __tablename__ = "s1"
>     id = Column(Integer, primary_key=True)
>     common_id = Column(ForeignKey("c.id"))
>     common_relationship = relationship("Common", uselist=False, 
> innerjoin=True, lazy='noload')
>     discriminator_field = Column(String)
>     __mapper_args__ = {
>         "polymorphic_identity": "superclass",
>         "polymorphic_on": discriminator_field,
>     }
>
>
> class Subclass(Superclass):
>     __tablename__ = "s2"
>     id = Column(ForeignKey("s1.id"), primary_key=True)
>     __mapper_args__ = {"polymorphic_identity": "subclass"}
>
> class Common(Base):
>     __tablename__ = "c"
>     id = Column(Integer, primary_key=True)
>     num_superclass = column_property(
>         select([func.count(Superclass.id)]).where(Superclass.common_id == 
> id).correlate_except(Superclass)
>     )
>
> e = create_engine("postgresql://jaychia:tiger@localhost/test", echo=True)
> Base.metadata.drop_all(e)
> Base.metadata.create_all(e)
>
> s = Session(e)
> s.add(Subclass(common_relationship=Common()))
> s.commit()
>
> poly = with_polymorphic(Superclass, "*")
> s.query(poly).options(contains_eager(poly.common_relationship)).join(
>     poly.common_relationship
> ).filter(Common.id == 1).all()
>
> The code above throws an error on the num_superclass column. Fixing the 
> num_superclass column to instead use the polymorphic version of Superclass 
> fixed it ! So the definition of Common should look like this instead:
>
> class Common(Base):
>     __tablename__ = "c"
>     id = Column(Integer, primary_key=True)
>
> poly = with_polymorphic(Superclass, "*")
> Common.num_superclass = column_property(
>     select([func.count(poly.id)]).where(poly.common_id == 
> Common.id).correlate_except(poly)
> )
>
> Am I doing this right? Seems like this is the best possible fix.

This is really weird and is probably a bug.   Send it as correlate()
for now which seems to do the right thing:

class Common(Base):
    __tablename__ = "c"
    id = Column(Integer, primary_key=True)


Common.num_superclass = column_property(
    select([func.count(Superclass.id)])
    .where(Superclass.common_id == Common.id)
    .correlate(Common)
)




>
> On Friday, March 8, 2019 at 8:53:35 PM UTC-8, Mike Bayer wrote:
>>
>> On Fri, Mar 8, 2019 at 3:56 PM <jay....@freenome.com> wrote:
>> >
>> > Hi everyone!
>> >
>> > I am trying to use the contains_eager option on a simple polymorphic query 
>> > that looks like:
>> >
>> > class Superclass():
>> >     common_relationship = relationship('Common', ...)
>> >     discriminator_field = Column(String...)
>> >     __mapper_args__ = {'polymorphic_identity': 'superclass', 
>> > 'polymorphic_on': discriminator_field}
>> >
>> > class Subclass(Superclass):
>> >     fields...
>> >
>> > poly = with_polymorphic(Superclass, '*')
>> > db.session.query(poly).options(contains_eager(poly.common_relationship)).join(poly.common_relationship).filter(Common.id
>> >  == 1).all()
>> >
>> > This code throws an error when I execute the query:
>> >
>> > sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) subquery uses 
>> > ungrouped column "common.id" from outer query
>>
>> this example is very vague and does not indicate what you are doing in
>> full.  Below is your example turned into a working mapping including
>> your exact query using Postgresql, works fine, and notably does not
>> generate a subquery which would only occur if there was more going on
>> above. Please provide a complete MCVE, most likely by modifying the
>> fully runnable code below, thanks!
>>
>> from sqlalchemy import Column
>> from sqlalchemy import create_engine
>> from sqlalchemy import ForeignKey
>> from sqlalchemy import Integer
>> from sqlalchemy import String
>> from sqlalchemy.ext.declarative import declarative_base
>> from sqlalchemy.orm import contains_eager
>> from sqlalchemy.orm import relationship
>> from sqlalchemy.orm import Session
>> from sqlalchemy.orm import with_polymorphic
>>
>> Base = declarative_base()
>>
>>
>> class Superclass(Base):
>>     __tablename__ = "s1"
>>
>>     id = Column(Integer, primary_key=True)
>>     common_id = Column(ForeignKey("c.id"))
>>
>>     common_relationship = relationship("Common")
>>     discriminator_field = Column(String)
>>     __mapper_args__ = {
>>         "polymorphic_identity": "superclass",
>>         "polymorphic_on": discriminator_field,
>>     }
>>
>>
>> class Subclass(Superclass):
>>     __tablename__ = "s2"
>>     id = Column(ForeignKey("s1.id"), primary_key=True)
>>     __mapper_args__ = {"polymorphic_identity": "subclass"}
>>
>>
>> class Common(Base):
>>     __tablename__ = "c"
>>     id = Column(Integer, primary_key=True)
>>
>>
>> e = create_engine("postgresql://scott:tiger@localhost/test", echo=True)
>> Base.metadata.drop_all(e)
>> Base.metadata.create_all(e)
>>
>> s = Session(e)
>> s.add(Subclass(common_relationship=Common()))
>> s.commit()
>>
>> poly = with_polymorphic(Superclass, "*")
>> s.query(poly).options(contains_eager(poly.common_relationship)).join(
>>     poly.common_relationship
>> ).filter(Common.id == 1).all()
>>
>> query at the end:
>>
>> 2019-03-08 23:50:11,847 INFO sqlalchemy.engine.base.Engine SELECT c.id
>> AS c_id, s1.id AS s1_id, s1.common_id AS s1_common_id,
>> s1.discriminator_field AS s1_discriminator_field, s2.id AS s2_id
>> FROM s1 LEFT OUTER JOIN s2 ON s1.id = s2.id JOIN c ON c.id = s1.common_id
>> WHERE c.id = %(id_1)s
>> 2019-03-08 23:50:11,847 INFO sqlalchemy.engine.base.Engine {'id_1': 1}
>>
>>
>>
>>
>> >
>> >
>> > Removing the contains_eager option and replacing it with a lazy or join 
>> > option does solve the problem, but introduces overhead to my SQL query. 
>> > What I am trying to achieve here is for the query to not perform two joins 
>> > (once for the join to filter on, and once for the join to populate the 
>> > poly.common_relationship field). Am I approaching this entirely wrong here?
>> >
>> > Thanks in advance!
>> >
>> > Jay
>> >
>> > This e-mail is private and confidential and is for the addressee only. If 
>> > misdirected, please notify us by telephone, confirming that it has been 
>> > deleted from your system and any hard copies destroyed. You are strictly 
>> > prohibited from using, printing, distributing or disseminating it or any 
>> > information contained in it save to the intended recipient.
>> >
>> > --
>> > 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.
>
>
> This e-mail is private and confidential and is for the addressee only. If 
> misdirected, please notify us by telephone, confirming that it has been 
> deleted from your system and any hard copies destroyed. You are strictly 
> prohibited from using, printing, distributing or disseminating it or any 
> information contained in it save to the intended recipient.
>
> --
> 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