Opened 21 months ago

Last modified 21 months ago

#34401 closed Bug

Inconsistent behavior for refresh_from_db() with GenericForeignKey — at Initial Version

Reported by: François Dupayrat Owned by: nobody
Component: contrib.contenttypes Version: 4.1
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Since Django 4, refresh_from_dbexhibit an inconsistent behavior for GenericForeignKey fields: sometime, the object field is properly refreshed, and sometime it isn't. That issue has been introduced with Django 4 and I've traced it back to this ticket: https://code.djangoproject.com/ticket/33008
and in particular those 2 lines in django/contrib/contenttypes/fields.py:
`

if rel_obj is None and self.is_cached(instance):

return rel_obj

`

With those 2 lines removed, the bug doesn't reproduce. I'm unsure if this is an acceptable solution for Django though.

Here is a minimal example to reproduce this bug.

models.py:
`
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models

class FirstType(models.Model):

pass

class SecondType(models.Model):

pass

class FirstOrSecond(models.Model):

association_type = models.ForeignKey(ContentType, blank=True, null=True, on_delete=models.SET_NULL, limit_choices_to=(

models.Q(app_label='generic_foreign_key_type', model='firsttype') |
models.Q(app_label='generic_foreign_key_type', model='secondtype')

))
associated_to_id = models.PositiveIntegerField(blank=True, null=True, db_index=True)
associated_to = GenericForeignKey('association_type', 'associated_to_id')

def change_association_to(obj_id, target):

matching_obj = FirstOrSecond.objects.filter(id=obj_id).first()
if matching_obj:

matching_obj.associated_to = target
matching_obj.save()

`

tests.py:
`
from django.test import TestCase
from .models import *

class GenericForeignKeysTests(TestCase):

def setUp(self):

self.first_or_second = FirstOrSecond.objects.create()
self.first = FirstType.objects.create()
self.second = SecondType.objects.create()

def test_refresh_from_db(self):

change_association_to(self.first_or_second.id, self.first)
self.first_or_second.refresh_from_db()
self.assertEqual(self.first_or_second.associated_to, self.first)

change_association_to(self.first_or_second.id, None)
self.first_or_second.refresh_from_db()
self.assertEqual(self.first_or_second.associated_to, None)

change_association_to(self.first_or_second.id, self.first)
self.first_or_second.refresh_from_db()
self.assertEqual(self.first_or_second.associated_to, self.first)

`

The last assertion fails because of this bug.

Change History (0)

Note: See TracTickets for help on using tickets.
Back to Top