[Answer]-Determine the class of a foreign key in Django

1👍

The fact is that when you query a model (via a QuerySet method, or indirectly via a ForeignKey) you get non-polymorphic instances – in contrast to SQLAlchemy, where you get polymorphic instances.

This is because the fetched data corresponds only to the data you’re accessing (and it’s ancestors since they are known beforehand). By default, Django does not do any kind of select_related to get the children, so you’re stuck with the base (i.e. current) class model of the foreign key or query set.

This means:

Vehicle.objects.get(pk=1).__class__ == Vehicle

will be always True, and:

Surprise.objects.get(pk=1).items.all()[0].__class__ == Vehicle

will be always True as well.

(assume for these examples that vehicle with pk=1 exists, surprise with pk=1 exists, and has at least one item)

There’s no clean solution for this EXCEPT by knowing your children classes. As you said: accessing variables like .car or .truck (considering classes Car and Truck exist) is the way. However if you hit the wrong children class (e.g. you hit vehicle.car when vehicle should be, actually, a Truck instance) you will get an ObjectDoesNotExist error. Disclaimer: Don’t know what would happen if you have two children classes with the same name in different modules.

If you want to have polymorphic behavior, which can abstract you from testing every possible subclass, an application exists (haven’t actually used it): https://django-polymorphic.readthedocs.org/en/latest/

0👍

According to Django Documentation:

If you have a Place that is also a Restaurant, you can get from the Place object to the Restaurant object by using the lower-case version of the model name:

p = Place.objects.get(id=12)
p.restaurant

Further to that:

However, if p in the above example was not a Restaurant (it had been created directly as a Place object or was the parent of some other class), referring to p.restaurant would raise a Restaurant.DoesNotExist exception.

So you answered the question on your own, you need to check the car attr, because that is what is pointing to the model you are looking for, if there is no car attr then the object was not created by the Car class.

0👍

As another workaround, I wrote this function that can be used for the same purpose without the need of django-polymorphic :

def is_model_instance(object, model):
    '''
    `object` is expected to be a subclass of models.Model
    `model`  should be a string containing the name of a models.Model subclass

    Return true if `object` has a reference to a `model` instance, false otherwise.
    '''
    model_name = model.lower()

    if model_name in dir(object):
        try:
            statement = "object." + model_name
            exec(statement)
            return True
        except object.DoesNotExist:
            return False
    else :
        return False

Then I can easily do something like

>>> is_model_instance(Surprise.objects.get(pk=1).items.all()[0], Car)
True     # this is indeed a Car

Leave a comment