Skip to content

Commit

Permalink
GenericForeignKey.__get__ use cached value even value is None
Browse files Browse the repository at this point in the history
  • Loading branch information
martinsvoboda committed Aug 19, 2021
1 parent 7a107b5 commit 7c83cec
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 5 deletions.
2 changes: 2 additions & 0 deletions django/contrib/contenttypes/fields.py
Expand Up @@ -228,6 +228,8 @@ def __get__(self, instance, cls=None):
pk_val = getattr(instance, self.fk_field)

rel_obj = self.get_cached_value(instance, default=None)
if rel_obj is None and self.is_cached(instance):
return rel_obj
if rel_obj is not None:
ct_match = ct_id == self.get_content_type(obj=rel_obj, using=instance._state.db).id
pk_match = rel_obj._meta.pk.to_python(pk_val) == rel_obj.pk
Expand Down
7 changes: 7 additions & 0 deletions tests/contenttypes_tests/models.py
Expand Up @@ -97,6 +97,13 @@ class Meta:
order_with_respect_to = 'parent'


class AuditLog(models.Model):
message = models.CharField(max_length=200)
content_type = models.ForeignKey(ContentType, models.CASCADE, null=True)
object_id = models.PositiveIntegerField(null=True)
object = GenericForeignKey()


class ModelWithNullFKToSite(models.Model):
title = models.CharField(max_length=200)
site = models.ForeignKey(Site, null=True, on_delete=models.CASCADE)
Expand Down
18 changes: 15 additions & 3 deletions tests/contenttypes_tests/test_fields.py
Expand Up @@ -2,14 +2,14 @@

from django.contrib.contenttypes.fields import GenericForeignKey
from django.db import models
from django.test import SimpleTestCase, TestCase
from django.test import TestCase
from django.test.utils import isolate_apps

from .models import Answer, Question
from .models import Answer, AuditLog, Question


@isolate_apps('contenttypes_tests')
class GenericForeignKeyTests(SimpleTestCase):
class GenericForeignKeyTests(TestCase):

def test_str(self):
class Model(models.Model):
Expand All @@ -24,6 +24,18 @@ def test_incorrect_get_prefetch_queryset_arguments(self):
with self.assertRaisesMessage(ValueError, "Custom queryset can't be used for this lookup."):
Answer.question.get_prefetch_queryset(Answer.objects.all(), Answer.objects.all())

def test_get_object_cache_respects_deleted_objects(self):
question = Question.objects.create(text="test")
question_pk = question.pk
log = AuditLog.objects.create(message="Created", object=question)
Question.objects.all().delete()

log = AuditLog.objects.get(message="Created")
with self.assertNumQueries(1):
self.assertEqual(log.object_id, question_pk)
self.assertIsNone(log.object)
self.assertIsNone(log.object)


class GenericRelationTests(TestCase):

Expand Down
4 changes: 2 additions & 2 deletions tests/prefetch_related/tests.py
Expand Up @@ -1041,8 +1041,8 @@ def test_deleted_object_GFK(self):
book1_pk = self.book1.pk
self.book1.delete()

with self.assertNumQueries(3):
qs = TaggedItem.objects.filter(tag='awesome').prefetch_related('content_object')
with self.assertNumQueries(2):
qs = TaggedItem.objects.filter(tag="awesome").prefetch_related("content_object")
result = [(t.object_id, t.content_type_id, t.content_object) for t in qs]
self.assertEqual(result, [
(book1_pk, ct.pk, None),
Expand Down

0 comments on commit 7c83cec

Please sign in to comment.