[Fixed]-Instantiating Django model raises TypeError: isinstance() arg 2 must be a class, type, or tuple of classes and types

20👍

UPDATE (with solution below)

I’ve been digging into the Django model code and it seems like there is a bug that creates a race condition when using “app.model”-based identifiers for the related field in a ForeignKey. When the application is running in production mode as opposed to DEBUG, the ForeignKey.get_default method referenced in the exception above tries to check if the provided default value is an instance of the related field (self.rel.to):

def get_default(self):
    "Here we check if the default value is an object and return the to_field if so."
    field_default = super(ForeignKey, self).get_default()
    if isinstance(field_default, self.rel.to):
        return getattr(field_default, self.rel.get_related_field().attname)
    return field_default

Initially when a ForeignKey is instantiated with a string-based related field, self.rel.to is set to the string-based identifier. There is a separate function in related.py called add_lazy_relation that, under normal circumstances, attempts to convert this string-based identifier into a model class reference. Because models are loaded in a lazy fashion, it’s possible that this conversion can be deferred until the AppCache is fully loaded.

Therefore, if get_default is called on a string-based ForeignKey relation before AppCache is fully populated, it’s possible for a TypeError exception to be raised. Apparently putting my application into production mode was enough to shift the timing of model caching that this error started occurring for me.

SOLUTION

It seems this is genuinely a bug in Django, but here’s how to get around it if you do ever run into this problem. Add the following code snippet immediately before you instantiate a troublesome model:

from django.db.models.loading import cache as model_cache
if not model_cache.loaded:
    model_cache._populate()

This checks the loaded flag on the AppCache to determine if the cache if fully populated. If it is not, we will force the cache to fully populate now. And the problem will be solved.

7👍

In my case i had a model ‘Entity’ and a ‘User’ which inherited ‘AbstractBaseUser’. A ‘User’ model had a entity field with ForeignKey to Entity configured that way:

entity = m.ForeignKey('core.Entity', on_delete=m.SET_NULL, null=True)

in the end the workaround was to change it to

from core.models import Entity
entity = m.ForeignKey(Entity, on_delete=m.SET_NULL, null=True)

I don’t know the cause of the problem, but it just works that way

1👍

Another cause:

Make sure that all Foreign Keys are in the same reference models in the same application (app label). I was banging my head against the wall for a while on this one.

class Meta:
    db_table = 'reservation'
    app_label = 'admin'

1👍

In my case, I made a mistake with the class call by surrounding the class name with quotes. Removing them solved the error for me. See the wrong code below:

from django.db import models
from django.contrib.auth.models import AbstractUser
from account.models import Profile

class User(AbstractUser):
    first_name = None;
    last_name = None;
    profile = models.ForeignKey(
        'Profile',
        on_delete=models.CASCADE,
    );

1👍

As it has already been highlighted in other answers, the most common cause seems to be referring to a model using the 'app.Model' syntax.

However, it might still be quite a big challenge to figure out which model exactly is causing the trouble.

I found that a quick solution to that question is to go directly to the place where the exception is raised (django.db.models.fields.related.ForeignKey.get_default) and add the following print statetements

    def get_default(self):
        """Return the to_field if the default value is an object."""
        field_default = super().get_default()
        # Add this line:
        print(f'Here is our small little bug: {field_default}, {self.remote_field.model}')
        if isinstance(field_default, self.remote_field.model):
            return getattr(field_default, self.target_field.attname)
        return field_default

Now the name of the problematic model is printed out and finding the relevant place in your code is no longer a big issue.

Tested on Django 2.2.

0👍

In most cases this error is caused by you referencing a deleted/commented model in another model with model relationship (Foreignkey etc).
Always check:

  1. If you have deleted or commented out some model(s)
  2. Check all your models for any foreignkey, onetoone, etc to a deleted model
  3. Be careful of where you reference a model lazily (example below) as python won’t raise any obvious error
center = models.ForeignKey("accounts.Center", blank=True, null=True,
                              on_delete=models.SET_NULL, related_name="staffs")
👤salafi

Leave a comment