On Sun, Nov 26, 2017 at 6:19 AM, Mischa S <mis...@jetbridge.com> wrote:
> I want to have a polymorphic table that gets its identity from a column in a
> "type" table. I have a "task" table and a "task_type" table. I want to be
> able to create task subclasses that say something like:
>
>     __mapper_args__ = {
>         'polymorphic_identity': 'do_stuff_task',
>     }
>
> (Assuming that a row with do_stuff_task is created in the task_type table)
>
>
> Currently in my base Task class I do:
>
>     task_type_name =
> column_property(select([TaskType.task_type]).where(TaskType.id ==
> task_type_id).as_scalar())
>
>     __mapper_args__ = {
>         'polymorphic_on': task_type_name,
>     }


this looks like you got this from
https://bitbucket.org/zzzeek/sqlalchemy/wiki/UsageRecipes/PolymorphicOnAnotherTable,
yup


>
>
> If I try to get say, a user.do_stuff_tasks relationship it emits the
> following SQL:
>
> SELECT
>   (SELECT task_type.task_type
>    FROM task_type
>    WHERE task_type.id = task.task_type_id)
> FROM task
> WHERE 123 = task.user_id
>   AND
>     (SELECT task_type.task_type
>      FROM task_type
>      WHERE task_type.id = task.task_type_id) IN ('do_stuff_task');
>
>
> This is technically correct, however this query is highly problematic for
> us. It does a scan on task in the subquery, which is extremely unpleasant
> and basically takes forever.
> What I want is something more like:
>
> SELECT *
> FROM task st
> JOIN task_type stt ON st.task_type_id=stt.id
> WHERE stt.task_type='do_stuff_task'
>   AND st.user_id=123;
>
> Is there some way to rewrite the column_property or polymorphic identity
> condition to combine the WHERE conditions in the JOIN instead of doing two
> subselects?

this would be very difficult to generalize. the
polymorphic_discriminator is only specified as a column expression and
in SQLAlchemy a column expression never implies a JOIN in the query, a
JOIN is always an explicit add on Query.    You could potentially
implement the before_compile() event for Query and add this JOIN
manually, but this would be very difficult to generalize to all cases.
  Here's how to do it for the demo on the wiki mentioned above:

from sqlalchemy import event
from sqlalchemy.orm.query import Query

@event.listens_for(Query, "before_compile", retval=True)
def setup_query_for_poly(query):
    entities = set(d['entity'] for d in query.column_descriptions)
    if A in entities:
        query = query.join(AType)
    return query


with the mapping like:

class A(Base):
    __tablename__ = 'a'

    id = Column(Integer, primary_key=True)
    data = Column(String)
    type_id = Column(ForeignKey('atype.id'))
    type_name = column_property(AType.name)
    type = relationship(AType)

    __mapper_args__ = {
        "polymorphic_on": type_name,
        "polymorphic_identity": "a"
    }


then you get a query like this:

SELECT atype.name AS atype_name, a.id AS a_id, a.data AS a_data,
a.type_id AS a_type_id
FROM a JOIN atype ON atype.id = a.type_id


but....if your query refers to "AType" elsewhere, or multiple times,
or you refer to "A" in different contexts, adding a JOIN like this
could get in the way.  Or if "A" is represented using aliasing, you'd
need to dig more carefully into query.column_descriptions and apply
the join() more carefully.

The other way to do this is just to put the polymorphic identities
into the mapper._polymorphic_map:

A.__mapper__.polymorphic_map.update(
    (key, A.__mapper__.polymorphic_map[value])
    for (key, value) in sess.query(AType.id, AType.name)
)

with mapping like:

class A(Base):
    __tablename__ = 'a'

    id = Column(Integer, primary_key=True)
    data = Column(String)
    type_id = Column(ForeignKey('atype.id'))
    type = relationship(AType)

    __mapper_args__ = {
        "polymorphic_on": type_id,
        "polymorphic_identity": "a"
    }






>
>
> Thanks!
>
> --
> 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