Awesome! I like the second approach better for the exact same reasons.
Thanks so much! Kent On Thursday, April 13, 2017 at 1:50:40 PM UTC-4, Mike Bayer wrote: > > > 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:> > <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+...@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. > > 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.