it has nothing to do with joined table inheritance, in your example,
your base mapper is already mapped to "preferences_union", so if you
provide an alternative selectable that has no relationship to that, it
does not see any of the required columns being provided. it's just
like if your PreferencesBase were mapped to a view in the database, it
would have no idea about the tables represented in that view. so while
it renders your new polymorphic union, it also renders the old one
because it still needs to load from preferences_union.c.preferenceid,
preferences_union.c.value, etc. which are not being substituted.
there's not a facility right now that can automatically handle the
scenario of, given:
SELECT a, b, c FROM (
select a, b, c FROM table1
UNION
select a, b, c FROM table2
) AS p_alias_1
that we want to make a whole new expression out of table1/table2:
SELECT a, b, c FROM (
select a, b, c FROM table1 WHERE b=1
UNION
select a, b, c FROM table2 WHERE c=2
) AS p_alias_2
and then "collide" the second into the first, such that we can figure
out that when our mapping wants p_alias_1.a, it can get that now from
p_alias_2.a, because this requires understanding the semantics of the
query. clause adaptation usually looks for the target columns you're
trying to adapt from in the target selectable, rather than trying to
match on a general "seems to link to the same common columns" as that
produces a lot of ambiguous cases.
*unless*, you adapt on the string name of the outer columns rather than
trying to link them up semantically. In this case, you as the user are
telling the system that you've already done the work of ensuring your
new selectable links to the first one the way you want, and you've lined
up the outermost column names as the means of doing this.
There is an "adapt_on_names" feature that does this, which is provided
as part of aliased(), where it matches p_alias_1.a to p_alias_2.a using
the string name "a". adapt_on_names is accepted by aliased(), but
currently not with_polymorphic() (this can be added, send a PR). So we
can build up w/ the AliasedClass directly:
from sqlalchemy.orm.util import AliasedClass
a = AliasedClass(
PreferenceBase,
u,
with_polymorphic_mappers=[
inspect(GlobalPreference),
inspect(SitePreference),
inspect(UserPreference)
],
with_polymorphic_discriminator=u.c.type,
adapt_on_names=True
)
so that's one way.
next approach, which I think is neater, is to do sort of what I
suggested but do it by adapting your original polymorphic, so that it
*does* line up. In the view metaphor, this means you're using the view
but swapping out the insides. this looks like this:
def apply_polymorphic_criteria(orig, target_table, criteria):
from sqlalchemy.sql import visitors
def provide_new_select(element):
if target_table in element.froms:
element.append_whereclause(criteria)
return visitors.cloned_traverse(
orig,
{},
{"select": provide_new_select}
)
u = apply_polymorphic_criteria(
preferences_union,
userpreferences_table,
userpreferences_table.c.username == 'kb'
)
u = apply_polymorphic_criteria(
u,
sitepreferences_table,
sitepreferences_table.c.siteid == '00'
)
a = with_polymorphic(PreferenceBase, '*', selectable=u,
polymorphic_on=u.c.type)
why I like that is because you don't have to build up the whole
polymorphic_union all over again. also the reliance on names (which
always makes me nervous) is not needed. I like that approach a lot
better but there's a little more rocket science involved.
also, I can't rememeber the last time I gave someone a neat little
recipe to do something unusual and it immediately fails in four other
ways and then I just have to write them a new feature, so, there's that,
try out both of these.
On 04/13/2017 12:39 PM, Kent wrote:
That was the first route I tried. with_polymorphic() seems to cater to
or assume joined table inheritance. When I pass a selectable, it always
ends up *joining *my base to that selectable instead of /using only my
selectable/.
My problem might be that I'm trying to take advantage of
polymorphic_union() to render my union with all the CAST(NULL AS ...),
but it does exactly what I need for building a select on the fly.
Attached script is my failing attempt.
Is there a recipe or example using concrete inheritance and
with_polymorphic?
On Thursday, April 13, 2017 at 10:43:15 AM UTC-4, Mike Bayer wrote:
On 04/13/2017 10:24 AM, Kent wrote:
> Suppose we have the documentation's example of *Concrete Table
> Inheritance, *where
>
> session.query(Employee).all()
>
>
> produces this:
>
> SELECT pjoin.type AS pjoin_type,
> pjoin.manager_data AS pjoin_manager_data,
> pjoin.employee_id AS pjoin_employee_id,
> pjoin.name <http://pjoin.name> AS pjoin_name, pjoin.engineer_info
AS pjoin_engineer_info
> FROM (
> SELECT employees.employee_id AS employee_id,
> CAST(NULL AS VARCHAR(50)) AS manager_data, employees.name
<http://employees.name> AS name,
> CAST(NULL AS VARCHAR(50)) AS engineer_info, 'employee' AS
type
> FROM employees
> UNION ALL
> SELECT managers.employee_id AS employee_id,
> managers.manager_data AS manager_data, managers.name
<http://managers.name> AS name,
> CAST(NULL AS VARCHAR(50)) AS engineer_info, 'manager' AS type
> FROM managers
> UNION ALL
> SELECT engineers.employee_id AS employee_id,
> CAST(NULL AS VARCHAR(50)) AS manager_data, engineers.name
<http://engineers.name> AS name,
> engineers.engineer_info AS engineer_info, 'engineer' AS type
> FROM engineers
> ) AS pjoin
>
>
> Suppose we want to*filter certain managers*, which we can do with:
>
> session.query(Employee)\
>
> .filter(or_(
>
> Employee.manager_data == u'whatineed',
>
> Employee.manager_data == None))\
>
> .all()
>
>
> If manager_data is indexed, many databases can no longer use this
index.
> What we really want is:
>
> SELECT pjoin.type AS pjoin_type,
> pjoin.manager_data AS pjoin_manager_data,
> pjoin.employee_id AS pjoin_employee_id,
> pjoin.name <http://pjoin.name> AS pjoin_name, pjoin.engineer_info
AS pjoin_engineer_info
> FROM (
> SELECT employees.employee_id AS employee_id,
> CAST(NULL AS VARCHAR(50)) AS manager_data, employees.name
<http://employees.name> AS name,
> CAST(NULL AS VARCHAR(50)) AS engineer_info, 'employee' AS
type
> FROM employees
> UNION ALL
> SELECT managers.employee_id AS employee_id,
> managers.manager_data AS manager_data, managers.name
<http://managers.name> AS name,
> CAST(NULL AS VARCHAR(50)) AS engineer_info, 'manager' AS type
> FROM managers
>
> *WHERE manager_data = 'whatineed'*
> UNION ALL
> SELECT engineers.employee_id AS employee_id,
> CAST(NULL AS VARCHAR(50)) AS manager_data, engineers.name
<http://engineers.name> AS name,
> engineers.engineer_info AS engineer_info, 'engineer' AS type
> FROM engineers
> ) AS pjoin
>
>
> Is there a way to accomplish this?
Certainly, construct the complete UNION query that you want, most
likely
using Core select() and union(), and supply it to Query using
with_polymorphic; see the "custom selectable" example in
http://docs.sqlalchemy.org/en/latest/orm/inheritance.html#basic-control-of-which-tables-are-queried
<http://docs.sqlalchemy.org/en/latest/orm/inheritance.html#basic-control-of-which-tables-are-queried>.
Automating this process, perhaps you could compose some enhanced
version
of the polymorhic_union() feature that accepts additional criteria.
However, I would suggest that if the "manager_data is NULL" part is
what
screws up the index, you might want to query like this instead:
session.query(Employee)\
.filter(or_(Employee.manager_data == u'whatineed',
pjoin.c.type != 'manager')).all()
>
> Thanks in advance,
> Kent
>
> --
> 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.