On 02/09/2017 01:54 PM, Shane Carey wrote:
Great, the aliasing works. Thanks so much. But, if I want to have my
design scale, how can I ask sqlalchemy to use a JOIN rather than a subquery?


for the polymorphic identity the way you're doing, there's not really a portable way to make it do a join every time...maybe if you loaded the JOIN into a SELECT and set that as a second argument into with_polymorphic for every subclass, as with_polymorphic allows an alternate selectable to be specified as well, though that is getting a little crazy.

It's a lot simpler if you can just pre-load that list of types up front and using the integer identifier inside something like a CASE statement. Then you don't need any joins or anything.

Looks like:

class Thing(Base):
    __tablename__ = 'thing'

    id = Column(Integer, primary_key=True)
    type_id = Column(Integer, ForeignKey(Type.id), nullable=False)

    type = relationship(Type)

    __mapper_args__ = {
        'with_polymorphic': '*'
    }

    @classmethod
    def _populate_polymorphic(cls, items):
        inspect(cls)._set_polymorphic_on(
            case(
                [
                    (
                        cls.type_id == literal_column(str(elem_id)),
                        literal_column("'%s'" % elem_name)
                    )
                    for elem_id, elem_name in items
                ]
            )
        )
        cls._polymorphic_lookup = dict(
            (elem_name, elem_id) for elem_id, elem_name in items
        )


@event.listens_for(Thing, 'init', propagate=True)
def set_identity(instance, *args, **kwargs):
    instance.type_id = instance._polymorphic_lookup[
        object_mapper(instance).polymorphic_identity]



then:

    Thing._populate_polymorphic(s.query(Type.id, Type.type).all())





On Thursday, February 9, 2017 at 10:38:42 AM UTC-6, Mike Bayer wrote:

    Running your example with

    'polymorphic_on':
                 select([Type.type]).where(Type.id == type_id).as_scalar()


    "res = s.query(Stuff).all()" works fine.

    The reason "res = s.query(Thing).join(Type).filter(Type.type ==
    'stuff').all()" fails is that Type is being injected into the SELECT
    statement twice and it's confused.

    the solution is to protect your subquery from interference by using an
    alias:

    type_alias = aliased(Type)


    class Thing(Base):
         __tablename__ = 'thing'

         id = Column(Integer, primary_key=True)
         type_id = Column(Integer, ForeignKey(Type.id), nullable=False)

         type = relationship(Type)

         __mapper_args__ = {
             'polymorphic_on':
                 select([type_alias.type]).where(type_alias.id
    <http://type_alias.id> ==
    type_id).as_scalar(),
             'with_polymorphic': '*'
         }


    then the join example at the end produces:

    SELECT thing.id <http://thing.id> AS thing_id, thing.type_id AS
    thing_type_id, (SELECT
    type_1.type
    FROM type AS type_1
    WHERE type_1.id <http://type_1.id> = thing.type_id) AS
    _sa_polymorphic_on, thing.stuff AS
    thing_stuff, thing.junk AS thing_junk
    FROM thing INNER JOIN type ON type.id <http://type.id> = thing.type_id
    WHERE type.type = %(type_2)s

    noting that this query is not going to scale up to millions of rows
    that
    well as MySQL hates those subqueries.







    On 02/09/2017 11:09 AM, Shane Carey wrote:
    > That is what I initially had,
    >
    > I get the error
    >
    > sqlalchemy.exc.InvalidRequestError: Select statement 'SELECT
    type.type
    > FROM type, thing
    > WHERE type.id <http://type.id> = thing.type_id' returned no FROM
    clauses due to
    > auto-correlation; specify correlate(<tables>) to control correlation
    > manually.
    >
    > On Thursday, February 9, 2017 at 10:02:40 AM UTC-6, Mike Bayer wrote:
    >
    >
    >
    >     On 02/09/2017 10:14 AM, Shane Carey wrote:
    >     > Hi, another question.
    >     >
    >     > I set my discriminator on the surrogate primary key of its
    table.
    >     > However, when I query for all of a certain discriminator, I
    get an
    >     error
    >     > 'Multiple rows returned for subquery'.
    >     >
    >     > Here is my canonical example
    >     >
    >     > from sqlalchemy import *
    >     > from sqlalchemy import select, and_, event, inspect
    >     > from sqlalchemy.orm import *
    >     > from sqlalchemy.ext.declarative import *
    >     >
    >     > Base = declarative_base()
    >     >
    >     > class Type(Base):
    >     > __tablename__ = 'type'
    >     >
    >     > id = Column(Integer, primary_key=True)
    >     > type = Column(String(8), unique=True)
    >     >
    >     > class Thing(Base):
    >     > __tablename__ = 'thing'
    >     >
    >     > id = Column(Integer, primary_key=True)
    >     > type_id = Column(Integer, ForeignKey(Type.id), nullable=False)
    >     >
    >     > type = relationship(Type)
    >     >
    >     > __mapper_args__ = { 'polymorphic_on':
    >     select([Type.type]).where(Type.id
    >     > == type_id).correlate(Type).as_scalar(), 'with_polymorphic':
    '*' }
    >     >
    >     > @event.listens_for(Thing, 'init', propagate=True)
    >     > def set_identity(instance, *args, **kwargs):
    >     > instance.type_id = select([Type.id]).where(Type.type ==
    >     > object_mapper(instance).polymorphic_identity)
    >     >
    >     > class Stuff(Thing):
    >     > stuff = Column(String(8))
    >     > __mapper_args__ = { 'polymorphic_identity': 'stuff' }
    >     >
    >     > class Junk(Thing):
    >     > junk = Column(String(8))
    >     > __mapper_args__ = { 'polymorphic_identity': 'junk' }
    >     >
    >     > if __name__ == '__main__':
    >     > e =
    create_engine('mysql+pymysql://user:password@localhost/test',
    >     echo=True)
    >     >
    >     > Base.metadata.drop_all(e)
    >     > Base.metadata.create_all(e)
    >     >
    >     > s = Session(e)
    >     >
    >     > s.add_all([Type(type='stuff'), Type(type='junk')])
    >     > for i in range(10):
    >     > s.add(Stuff(stuff='stuff_{}'.format(i)))
    >     > s.add(Junk(junk='junk_{}'.format(i)))
    >     >
    >     > s.commit()
    >     >
    >     > res = s.query(Thing).join(Type).filter(Type.type ==
    'stuff').all()
    >     >
    >     > #res = s.query(Stuff).all() Also fails
    >     >
    >     > At first I was receiving the error 'No FROM table specified
    due to
    >     > autocorrelate', but I was able to solve that with
    'polymorphic_on':
    >     > select([Type.type]).where(Type.id ==
    >     type_id).correlate(Type).as_scalar()
    >     > which I still do not fully understand. The exact error I get is
    >
    >     I think you want to correlate(Thing) there.  correlate(X)
    means you
    >     don't want "X" in the FROM list in the subquery.    But also the
    >     correlation should be automatic here so you wouldn't need to
    refer to
    >     "Thing" in the class def (if you did, you'd need to turn
    >     __mapper_args__
    >     into a @declared_attr).
    >
    >     that is:
    >
    >     "polymorphic_on": select([Type.type]).where(Type.id ==
    >     type_id).as_scalar()
    >
    >     should work
    >
    >
    >
    >     >
    >     > sqlalchemy.exc.InternalError: (pymysql.err.InternalError)
    (1242,
    >     > 'Subquery returns more than 1 row') [SQL: 'SELECT thing.id
    <http://thing.id>
    >     <http://thing.id> AS thing_id,
    >     > thing.type_id AS thing_type_id, thing.text AS thing_text,
    (SELECT
    >     > type.type \nFROM type, thing \nWHERE type.id
    <http://type.id> <http://type.id> =
    >     thing.type_id) AS
    >     > _sa_polymorphic_on, thing.stuff AS thing_stuff, thing.junk AS
    >     thing_junk
    >     > \nFROM thing']
    >     >
    >     > it seems like the SQL it should generate is
    >     >
    >     > SELECT thing.id <http://thing.id> <http://thing.id> AS
    thing_id,
    >     > thing.type_id AS thing_type_id,
    >     > thing.text AS thing_text,
    >     > (
    >     >     SELECT type
    >     >     FROM type # this was thing before
    >     >     WHERE type.id <http://type.id> <http://type.id> =
    thing.type_id
    >     > ) AS _sa_polymorphic_on,
    >     > thing.stuff AS thing_stuff,
    >     > thing.junk AS thing_junk
    >     > FROM thing
    >     > INNER JOIN type ON type.id <http://type.id> <http://type.id>
    = thing.type_id
    >     > WHERE type.type = 'junk';
    >     >
    >     > but better again would be, this, right?
    >     >
    >     > SELECT thing.id <http://thing.id> <http://thing.id> AS
    thing_id,
    >     > thing.type_id AS thing_type_id,
    >     > thing.text AS thing_text,
    >     > type.type AS _sa_polymorphic_on,
    >     > thing.stuff AS thing_stuff,
    >     > thing.junk AS thing_junk
    >     > FROM thing
    >     > INNER JOIN type ON type.id <http://type.id> <http://type.id>
    = thing.type_id
    >     > WHERE type.type = 'junk';
    >     >
    >     > so I am not sure what is going wrong or how I can generate the
    >     correct SQL,
    >     > or if possible generate a join rather than a subquery.
    >     >
    >     > Thanks for you help and patience
    >     >
    >     > --
    >     > 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
    <http://stackoverflow.com/help/mcve>
    >     <http://stackoverflow.com/help/mcve
    <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 <javascript:>
    >     > <mailto:sqlalchemy+unsubscr...@googlegroups.com
    <javascript:> <javascript:>>.
    >     > To post to this group, send email to sqlal...@googlegroups.com
    >     <javascript:>
    >     > <mailto:sqlal...@googlegroups.com <javascript:>>.
    >     > Visit this group at
    https://groups.google.com/group/sqlalchemy
    <https://groups.google.com/group/sqlalchemy>
    >     <https://groups.google.com/group/sqlalchemy
    <https://groups.google.com/group/sqlalchemy>>.
    >     > For more options, visit https://groups.google.com/d/optout
    <https://groups.google.com/d/optout>
    >     <https://groups.google.com/d/optout
    <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
    <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 <javascript:>
    > <mailto:sqlalchemy+unsubscr...@googlegroups.com <javascript:>>.
    > To post to this group, send email to sqlal...@googlegroups.com
    <javascript:>
    > <mailto:sqlal...@googlegroups.com <javascript:>>.
    > Visit this group at https://groups.google.com/group/sqlalchemy
    <https://groups.google.com/group/sqlalchemy>.
    > For more options, visit https://groups.google.com/d/optout
    <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
<mailto:sqlalchemy+unsubscr...@googlegroups.com>.
To post to this group, send email to sqlalchemy@googlegroups.com
<mailto: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