Eager loading broken using overloaded relationship

Issue #4094 closed
Julio César Gázquez
created an issue

I did adapt my model as suggested by @Michael Bayer in the mailing list and documented in Bitbucket issue #4078, declaring relationships as viewonly=True in a superclass and overriding them as viewonly=False in subclasses as needed.

Then I found that when doing a query on a subclass eager loading one of those relationships works fine. However doing the same query on the superclass results in the SQL query correctly issued, but the related objects for one of those relationships are missing (not accesible through the relationship attribute, and not even loaded in the session).

I can only add that this isn't a regression as I get the same results on 1.1.14 and 0.9.9, and that removing the relationship overload in the subclass (and removing viewonly=True in the superclass) I got it working normally. The bug is database agnostic, tested with PostgreSQL and SQLite.

Test case attached.

Comments (5)

  1. Michael Bayer repo owner

    This behavior is (mostly) expected. When you say to joinedload "a_member", the "B" class has no such member. Right now, the internals are still partially figuring out that "a_member" belongs to a "B" subclass, which is why no error is raised and the SQL is emitted, but this part of it is likely a bug because this is not enough information for the ORM to actually load the object.

    To specify attributes that are specific to BSub1 you need to name BSub1 and also make sure that the load for "B" is polymoprhic, so that it knows to look for attributes that are on subclasses too:

    b1 = (
        session.query(B).with_polymorphic('*')
            .options(
                orm.joinedload('a'),
                orm.joinedload(BSub1.a_member))
            .first())
    

    There are at least three ways to set up the polymorphic part of this, one is as above, another is to add "with_polymorphic": "*" to B.__mapper_args__, another is to use the with_polymorphic standalone feature.

    Also the viewonly=True aren't needed in the example as given, not sure if this is only a segment of the more complicated thing we worked out in #4078.

    The part here that might be a bug, I will examine furher:

    1. if the query has no intention of loading based on joinedload('a_member'), it should raise, just as though we had said joinedload('foo')

    There's also a not totally intuitive thing, but I think not aa bug:

    1. When we do specify with_polymorphic, now we know what "a_member" is. we should be able to load it

    But, if another BSub also had a_member, then this would again be ambiguous. So joinedload("a_member") with a string should pretty much raise in all cases.

  2. Michael Bayer repo owner

    Well OK, is there a reason BSub1 has a separate a_member? this use case would make more sense if you could show me what is supposed to be different about BSub1.a_member. Because this still makes sense; you are getting back a BSub1 object, and BSub1.a_member has nothing to do with B.a_member. Without specifying that you want BSub1.a_member, the ORM correctly does not apply the remainder of the row to the BSub1 object. the query includes the extra columns on behalf of B.a_member, but no rows match because there are no B objects that are not BSub1.

    It looks like the ORM is doing amazingly well at making the exact right decisions in this complex case, in fact.

  3. Julio César Gázquez reporter

    The reason BSub1 has a separate a_member is because I was too smart for my own good. In some subclasses of B I want to set "a", on others I want to set a_member and get "a" implicitly set when persisted because both relationships share b.a_id in their foreign keys.

    I already set out the rationale in the mailing list, that took you to open issue #4078 where you streamlined my example.

    But now I understand I B.a_member and BSub1.a_member (and B.a and BSub1.a) are different relationships, and I need a different approach.

    Thanks for your time.

  4. Log in to comment