this is a bug, for which the "correlate()" workaround I gave you can be used for now. The fix is tracked from https://github.com/sqlalchemy/sqlalchemy/issues/4537 and will be in release 1.3.2.
On Sun, Mar 10, 2019 at 10:32 PM Mike Bayer <mike...@zzzcomputing.com> wrote: > > 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.