passing a polymorphic subclass into __mapper_args__['with_polymorphic'] ?

85 views
Skip to first unread message

Gerald Thibault

unread,
Dec 7, 2012, 7:56:13 PM12/7/12
to sqlal...@googlegroups.com
I have a test file (attached below) with a base class called Test, and an extended class called PolyTest, which is a polymorphic subclass of Test.

I want to query for Test and get back a full PolyTest object. I do not want to use with_polymorphic: '*', as we have a large number of subclasses, only one of which will ever be used at a time (when the fastcgi environ is set, the subclass is assigned, and will not change for the life of the server). Each subclass is a globally used thing, so for client A, every instance of Test will always be TestA, which has that client's unique params. Using * on with_poly generated a monstrous query spanning every subtable we have, and is not an option.

So, given that PolyTest inherits from Test (implying test needs to be mapped first), how do I pass the subclass PolyTest into the mapper_args of Test? There is not a single example on the entire site which provides guidance, as every single occurrence of with_polymorphic occurring within mapper args is using '*'. This makes me wonder if it is even possible, because I can't find a single example anywhere of someone passing classes into mapper_args['with_poly'].

In the enclosed test, the final print should have a value attribute in it, which it does not.

Also, using query.with_polymorphic is not a reasonable option here, as we have a huge platform which expects to query() and get back the exact result. We're not planning on replacing every instance of 'query()' in our codebase.
with_poly_test.py

Michael Bayer

unread,
Dec 7, 2012, 8:39:45 PM12/7/12
to sqlal...@googlegroups.com
the history of with_polymorphic spans back to the time when we didn't have declarative and just the mapper() call was used, at which point any particular subclass would also be available, and yes you certainly can pass a specific list of classes to the mapper-level with_polymorphic - the with_polymorphic argument on mapper() takes the exact same argument format as the with_polymorphic() method on Query.

It's the declarative system that is not letting you do it, as this is not a common use case, but the reason is because the hook you're using is called during the creation of the Test class, necessarily before any subclass of Test exists.

One thing hit me about your use case - that a particular Python environment will only use one of these subclasses at a time.    The ORM has less guesswork to do if you in fact use the PolyTest class directly in queries, instead of querying against the base Test.    If it were me, I'd not bother with "polymorphic" at all, and just set up a reference to the class that the app needs:

class Test(...):
    ....

class PolyTest(...):
   ...

# fastcgi environment thing:

MyClass = PolyTest # (or PolyTest2, or PolyTest18, whatever your fastcgi thing sets up)

Using the subclass directly has the advantage that you can directly refer to those attributes that are specific to PolyTest in queries without switching classes.  Suppose Test has a column "x" and PolyTest has a column "y".   The with_polymorphic approach might have you doing this kind of thing:

query(Test).filter(PolyTest.y == 'value').filter(Test.x == 'value')

whereas if you just use PolyTest, or some alias, simpler:

query(MyClass).filter(MyClass.y == 'value').filter(MyClass.x == 'value')


Anyway, if you don't want to do that, then just set with_polymorphic after the fact, which you can do using _set_with_polymorphic():

Test.__mapper__._set_with_polymorphic([PolyTest])

_set_with_polymorphic() was added in the middle of the 0.7 series to support the ConcreteBase system, which has this same need, that is, to set up with_polymorphic on the base class of a mapper hierarchy.   You can also do it via your declarative using __declare_last__():

class Test(...):
    @classmethod
    def __declare_last__(cls):
        m = cls.__mapper__

# don't set it twice, if multiple mapper configs occur
        if m.with_polymorphic:
            return

        m._set_with_polymorphic([PolyTest])

__declare_last__() is basically a class-level hook that's triggered by the "after_configured" mapper event, which you could also use directly.






--
You received this message because you are subscribed to the Google Groups "sqlalchemy" group.
To view this discussion on the web visit https://groups.google.com/d/msg/sqlalchemy/-/l_78KhKtxogJ.
To post to this group, send email to sqlal...@googlegroups.com.
To unsubscribe from this group, send email to sqlalchemy+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/sqlalchemy?hl=en.
<with_poly_test.py>

Reply all
Reply to author
Forward
0 new messages