django-polymorphic icon indicating copy to clipboard operation
django-polymorphic copied to clipboard

get_real_instance_class throws TypeError

Open korkmaz opened this issue 8 years ago • 15 comments

There is a chance that polymorphic_ctype_id of an object can point a wrong non-proxy ContentType model. If that case happens polymorphic model's get_real_instance_class throws a TypeError instead of RuntimeError.

issubclass method is called with a None argument like issubclass(model_instance, None)

# Protect against bad imports (dumpdata without --natural) or other
# issues missing with the ContentType models.
if model is not None \
and not issubclass(model, self.__class__) \
and not issubclass(model, self.__class__._meta.proxy_for_model):
    raise RuntimeError("ContentType {0} for {1} #{2} does not point to a subclass!".format(
        self.polymorphic_ctype_id, model, self.pk,
    ))
    return model

Maybe above code should also check self.__class__._meta.proxy_for_model is a None or not before calling issubclass?

korkmaz avatar Mar 14 '16 22:03 korkmaz

Hi thanks for reporting this! Could you write a test that replicates this problem? That would help a lot to write a proper solution.

vdboor avatar May 04 '16 09:05 vdboor

I am experiencing similar problems. While i am able to reproduce in a particular system, i don't know how to re-create this in an encapsulated environment

amitnabarro avatar Jun 08 '16 10:06 amitnabarro

I found out a descrepency in the polymorphic_ctype_id values in the base class. I still don't know what caused the polymorphic object to point to the wrong CotentType object, but at least it explains why the call fails

amitnabarro avatar Jun 08 '16 13:06 amitnabarro

I am hitting this too. Have you solved the problem?

thenewguy avatar Oct 01 '16 19:10 thenewguy

Some how for me it appears that the base class is being saved. Then an exception occurs during creating the child. And the base class stays in the database but you can't delete it due to this error. Not sure how this is happening

thenewguy avatar Oct 01 '16 20:10 thenewguy

or perhaps the child class is being deleted and the base class is not. But some how the base class exists without a child references. I am verifying this in the admin using the polymorphic filters

thenewguy avatar Oct 01 '16 20:10 thenewguy

I did find out what my problem was for me, which was unrelated to django-polymorphic directly. I am implementing a multi-tenant system using Postgres schemas. As it turns out, you need to be really careful with content types when doing that. Since django-polymorphic relies on it (polymorphic_ctype_id in the base class) one has to make sure that if multiple schemas do not use the same content-type table, then clearing the content-type cache at every request is important. The only problem related to django-polymorphic was the cryptic error message

amitnabarro avatar Oct 03 '16 16:10 amitnabarro

Hrm... well I am only using one content type table.

Any idea how to clean up when this happens? It rarely seems to happen but it breaks any code that happens to retrieve one of these records from the database... which is bad on a list view. And I can't seem to delete the records due to the exception

thenewguy avatar Oct 04 '16 13:10 thenewguy

Since I use Postgres i simply used pgAdmin to find the rogue record and modify it. You can easily do a select distinct query on the polymorphic_ctype_id parameter to see if you even have bad records and then another query to see how many of them are. Well, thats what I did in order to fix my records

amitnabarro avatar Oct 04 '16 13:10 amitnabarro

Sounds simple enough. Thanks for the tip

thenewguy avatar Oct 04 '16 14:10 thenewguy

I've faced with the same exception when I was trying to open admin page for sub-model. After about 1 hour of mindgames I figured out what is the root cause.

My migration was applied incorrectly: parent and sub-entities had same value for the "polymorphic_ctype" and, as result, type of all the sub-entities was recognized as type of parent. That happened because of I copy-pasted migration from here.

The solution is to change forwards_func source code to handle existing parent-child inheritance relationships as follows:

from django.db import migrations, models
from django.db.models import Q

def forwards_func(apps, schema_editor):
    models = apps.get_app_config('projectid').get_models()
    content_type = apps.get_model('contenttypes', 'ContentType')
    for model in models:
        new_ct = content_type.objects.get_for_model(model)
        objects = model.objects.filter(
            Q(polymorphic_ctype__isnull=True)
            | ~Q(polymorphic_ctype=new_ct))

        objects.update(polymorphic_ctype=new_ct)

class Migration(migrations.Migration):
    .... and etc.

My env:

Django==2.0.4
django-polymorphic==2.0.3
django-rest-polymorphic==0.1.7

vsfedorenko avatar Sep 20 '18 00:09 vsfedorenko

I've come across this as well 👍

At first I assumed a version issue between django-polymorphic, django-filer, django.

The polymorphic_ctype_id for an object in my database links to a content type which isn't a polymorphic (or proxy) model.

This particular project is still in development so databases have been passed between a couple of developers while jobs have been done, so I'm not sure if an object was created in this table, at which point the ctype was a poly/proxy model. Then after that point the databases has been updated by someone else, moved around again & now that ctype isn't a poly model. I suspect this because the newer objects of the same type have a different polymorphic_ctype_id to that original object.

Surely it makes sense for django-polymorphic to fail gracefully in this instance. I've got a more explicit solution, so will setup a fork with a PR & some tests.

marksweb avatar Nov 22 '18 10:11 marksweb

I ran into this while migrating data from sqlite3 to PostgreSQL. I had to look at the django_content_type table. I examined the old table and compared it to the new table. It was out of order. Basically I had child models pointing to the wrong content types. So have a look at your django_content_type table if you are running into this.

teewuane avatar Dec 12 '18 22:12 teewuane

We are experiencing the same problem. We use Postgresql Schemas and uses request middleware to switch between different schemas.

Keep in mind that the Model -> Content Type ID links differ from schema to schema due to new models made after the first tenant was created.

class File(PolymorphicModel, mixins.IconsMixin):

When I restart UWSGI and then on Tenant 1 goto the Filer->Folder page in the admin then it works well. Now when I go to Tenant 2 then it seems that django-polymorphic is trying to use the content type id of the File model in Tenant 1 to lookup the real instance class.

Could it be that the ctype_id is being cached somehow?

JacoBezuidenhout avatar Oct 03 '19 12:10 JacoBezuidenhout

I'm facing the same issue when calling a polymorphic class has anyone found a solution

nassim-fox avatar May 18 '21 08:05 nassim-fox