On Thu, Sep 14, 2023, at 7:36 AM, Cornelis Poppema wrote:
> Hi all,
> 
> I am new to sqlalchemy, I think the idea of what I am trying to achieve is 
> relatively simple, but I can't seem to figure out how to retrieve `.service` 
> in the same query. I failed to find an example in the 2.0 documentation for 
> exactly this.
> 
> My attempts have been to simply chain a .selectinload after the 
> .selectin_polymorphic, ie.:
> 
> ```python
>         .options(
>             selectinload(StepModel.next_step, recursion_depth=-1),
>             selectinload(StepModel.actionbases).selectin_polymorphic(
>                 [
>                     ServiceActionModel,
>                 ],
>             )
>             .selectinload(ServiceActionModel.service),
>             raiseload("*"),
>         )
> ```
> 
> This gives the error:
> 
> ```
> /usr/local/lib/python3.11/site-packages/sqlalchemy/orm/strategy_options.py:2442:
>  in _raise_for_does_not_link
>     raise sa_exc.ArgumentError(
> E   sqlalchemy.exc.ArgumentError: ORM mapped entity or attribute 
> "ServiceActionModel.service" does not link from relationship 
> "StepModel.actionbases".  Did you mean to use 
> "StepModel.actionbases.of_type(ServiceActionModel)"?
> ```

Hi -

A few up front things, is it possible you can post these questions that have a 
lot of code on github discussions?   That's at 
https://github.com/sqlalchemy/sqlalchemy/discussions .    I'm sort of wondering 
how new users are still arriving here at the mailing list, did you find this 
list via the support page at https://www.sqlalchemy.org/support.html ?   I 
would want to change the verbiage there to please refer people to GH 
discussions instead.    Especially with these advanced inheritance eager 
loading problems, which in the majority of cases end up being real bugs in 
SQLAlchemy, as seems to be the case here (at least, there is an inconsistency 
in the API that somehow needs to be documented, or something).

As for the question, first off this is really advanced usage and I've hardly 
ever seen people using selectin_polymorphic(), much less deep within a chain of 
loaders like this.

The correct form for this load would follow from how it's described at 
https://docs.sqlalchemy.org/en/20/orm/queryguide/inheritance.html#combining-additional-loader-options-with-selectin-polymorphic-subclass-loads
 , where the ORM allows the selectin_polymorphic(Target, [TargetSubclassA]) to 
be a sibling to the appropriate relationship load, 
selectinload(TargetSubclassA.elements).   The example there places both of 
these options comma-separated within select().options().    This is the 
"inconsistent" part because I'm already surprised the ORM is allowing the 
selectinload() to be present against TargetSubclassA when that's not one of the 
primary entities in the select().

However in your case, you are coming off of a parent loader option already.  So 
following from this, the correct form based on a direct reading of those docs 
would, *in theory*, be:

select().options(
     selectinload(Parent.target).options(
             selectin_polymorphic(Target, [TargetSubclassA]),
             selectinload(TargetSubclassA.elements)
     )
)

that is, you can build up sibling options from a parent loader option using 
another call to .options().

however, this doesn't work; the validation of the loader chain unsurprisingly 
notes that TargetSubclass is not linked from Parent.target, and they'd like you 
to use of_type() instead.    So I've made a bug for this here:  
https://github.com/sqlalchemy/sqlalchemy/issues/10348  as something has to 
change here, either the docs, or the usage pattern for selectin_polymorphic(), 
or the error checks have to get a lot smarter to figure this out and let this 
case pass, since it works fine the way you are expecting if I just have it skip 
the error checking.

What you can do now is use with_polymorphic() instead that is more configurable 
for this kind of loader chain:

TT = with_polymorphic(Target, [TargetSubclassA])
select().options(selectinload(Parent.target.of_type(TT).selectinload(TT.TargetSubclassA.elements)))

discussion can continue at the issue above or a new github discussion, 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 view this discussion on the web visit 
https://groups.google.com/d/msgid/sqlalchemy/a99d9853-8936-455f-9439-bfe5f2e03faa%40app.fastmail.com.

Reply via email to