Opened 15 months ago

Last modified 14 months ago

#34816 closed Bug

GenericForeignKey crashes if content_type_id is changed and object_id is type incompatible with old object — at Initial Version

Reported by: Richard Laager Owned by: nobody
Component: contrib.contenttypes Version: 4.2
Severity: Normal Keywords:
Cc: Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Steps to reproduce:

  1. Create a model ("A") with a GenericForeignKey.
  2. Create an instance of A ("a") referencing an instance of model "B" with a PK that is an integer type.
  3. Change the instance of "A" to reference an instance of model "C" with a PK that is incompatible with int(). But make this change using content_type_id and object_id properties, not content_object, i.e.:
        a.content_type_id = ContentType.objects.get_for_model(B)
        a.object_id = "foo"
    
  4. Try to access a.content_object.

Expected result:

This looks up and returns an instance of "b" (the B with a PK of "foo").

Actual result:

This crashes (I'm using 3.2, but the code is unchanged in master):

  File "django/db/models/fields/__init__.py", line 1836, in to_python
    return int(value)

  ValueError: invalid literal for int() with base 10: 'foo'

This happens because it tries to to_python() the new key on the old model's PK field.

One possible fix would be to make the logic short-circuit, like this (also attached):

  • django/contrib/contenttypes/fields.py

    diff --git a/django/contrib/contenttypes/fields.py b/django/contrib/contenttypes/fields.py
    index 35fcd0d908..e984fb5375 100644
    a b class GenericForeignKey(FieldCacheMixin):  
    242242            ct_match = (
    243243                ct_id == self.get_content_type(obj=rel_obj, using=instance._state.db).id
    244244            )
    245             pk_match = rel_obj._meta.pk.to_python(pk_val) == rel_obj.pk
    246             if ct_match and pk_match:
    247                 return rel_obj
    248             else:
    249                 rel_obj = None
     245            if ct_match:
     246                pk_match = rel_obj._meta.pk.to_python(pk_val) == rel_obj.pk
     247                if pk_match:
     248                    return rel_obj
     249            rel_obj = None
    250250        if ct_id is not None:
    251251            ct = self.get_content_type(id=ct_id, using=instance._state.db)
    252252            try:

Change History (1)

by Richard Laager, 15 months ago

Attachment: gfk-crash.patch added
Note: See TracTickets for help on using tickets.
Back to Top