Why Django's related_model property is returning string instead of Model instance?

1.6k Views Asked by At

I have some strange behaviour, at least for me, that is causing me some bugs in my project.

I am using Django 1.9 and a third party django package (django-jet) that makes usage of field.related_model property in Django admin and sometimes it fails because it expects field.related_model returns a model instance and for some of my models is returning the model name.

This is the property defined in Django code:

@cached_property
def related_model(self):
    # Can't cache this property until all the models are loaded.
    apps.check_models_ready()
    return self.remote_field.model

Things that I tried:

  • If Django's related_model is a @property instead a @cached_property it works and returns the model instance.
  • If I call field.remote_field.model instead of field.related_model in the line that is causing the bug it works and returns the model instance.

Please, do you have any idea? I can make the workaround but I would like to know why this behaviour.

Thanks in advance!

1

There are 1 best solutions below

0
On BEST ANSWER

I think the problem here arises because jet is trying to use related_model in the RelatedFieldAjaxListFilter.field_choices() method, and this may executed before all apps have been loaded. If I understand correctly, the related_model value is initially a string, which is replaced with a model object in the course of model initialization. If you try to get that value before the apps have all loaded, you may get either a string or an object, depending on the order in which the models are loaded. And, since it's a cached property, getting a string value at that stage would cause the string value to be cached. See, for example, the comment in django.db.models.options,

# The mechanism for getting at the related model is slightly odd -
# ideally, we'd just ask for field.related_model. However, related_model
# is a cached property, and all the models haven't been loaded yet, so
# we need to make sure we don't cache a string reference.

By making related_name an uncached property, you avoid that issue.

In the django.contrib.admin.filters.RelatedFieldListFilter code, they do NOT use related_model to get the model object, but instead use a utility function, django.contrib.admin.utils.get_model_from_relation(). The RelatedFieldAjaxListFilter.field_choices() should probably be doing something similar.