Fixed #33008 -- Fixed prefetch_related() for deleted GenericForeignKeys.
Thanks Simon Charette for the implementation idea.
This commit is contained in:
parent
cdad96e633
commit
cc4cb95bef
|
@ -213,7 +213,7 @@ class GenericForeignKey(FieldCacheMixin):
|
||||||
gfk_key,
|
gfk_key,
|
||||||
True,
|
True,
|
||||||
self.name,
|
self.name,
|
||||||
True,
|
False,
|
||||||
)
|
)
|
||||||
|
|
||||||
def __get__(self, instance, cls=None):
|
def __get__(self, instance, cls=None):
|
||||||
|
@ -229,6 +229,8 @@ class GenericForeignKey(FieldCacheMixin):
|
||||||
pk_val = getattr(instance, self.fk_field)
|
pk_val = getattr(instance, self.fk_field)
|
||||||
|
|
||||||
rel_obj = self.get_cached_value(instance, default=None)
|
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:
|
if rel_obj is not None:
|
||||||
ct_match = ct_id == self.get_content_type(obj=rel_obj, using=instance._state.db).id
|
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
|
pk_match = rel_obj._meta.pk.to_python(pk_val) == rel_obj.pk
|
||||||
|
|
|
@ -2,14 +2,14 @@ import json
|
||||||
|
|
||||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.test import SimpleTestCase, TestCase
|
from django.test import TestCase
|
||||||
from django.test.utils import isolate_apps
|
from django.test.utils import isolate_apps
|
||||||
|
|
||||||
from .models import Answer, Question
|
from .models import Answer, Post, Question
|
||||||
|
|
||||||
|
|
||||||
@isolate_apps('contenttypes_tests')
|
@isolate_apps('contenttypes_tests')
|
||||||
class GenericForeignKeyTests(SimpleTestCase):
|
class GenericForeignKeyTests(TestCase):
|
||||||
|
|
||||||
def test_str(self):
|
def test_str(self):
|
||||||
class Model(models.Model):
|
class Model(models.Model):
|
||||||
|
@ -24,6 +24,19 @@ class GenericForeignKeyTests(SimpleTestCase):
|
||||||
with self.assertRaisesMessage(ValueError, "Custom queryset can't be used for this lookup."):
|
with self.assertRaisesMessage(ValueError, "Custom queryset can't be used for this lookup."):
|
||||||
Answer.question.get_prefetch_queryset(Answer.objects.all(), Answer.objects.all())
|
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='Who?')
|
||||||
|
post = Post.objects.create(title='Answer', parent=question)
|
||||||
|
|
||||||
|
question_pk = question.pk
|
||||||
|
Question.objects.all().delete()
|
||||||
|
|
||||||
|
post = Post.objects.get(pk=post.pk)
|
||||||
|
with self.assertNumQueries(1):
|
||||||
|
self.assertEqual(post.object_id, question_pk)
|
||||||
|
self.assertIsNone(post.parent)
|
||||||
|
self.assertIsNone(post.parent)
|
||||||
|
|
||||||
|
|
||||||
class GenericRelationTests(TestCase):
|
class GenericRelationTests(TestCase):
|
||||||
|
|
||||||
|
|
|
@ -1033,6 +1033,24 @@ class GenericRelationTests(TestCase):
|
||||||
# instance returned by the manager.
|
# instance returned by the manager.
|
||||||
self.assertEqual(list(bookmark.tags.all()), list(bookmark.tags.all().all()))
|
self.assertEqual(list(bookmark.tags.all()), list(bookmark.tags.all().all()))
|
||||||
|
|
||||||
|
def test_deleted_GFK(self):
|
||||||
|
TaggedItem.objects.create(tag='awesome', content_object=self.book1)
|
||||||
|
TaggedItem.objects.create(tag='awesome', content_object=self.book2)
|
||||||
|
ct = ContentType.objects.get_for_model(Book)
|
||||||
|
|
||||||
|
book1_pk = self.book1.pk
|
||||||
|
self.book1.delete()
|
||||||
|
|
||||||
|
with self.assertNumQueries(2):
|
||||||
|
qs = TaggedItem.objects.filter(tag='awesome').prefetch_related('content_object')
|
||||||
|
result = [
|
||||||
|
(tag.object_id, tag.content_type_id, tag.content_object) for tag in qs
|
||||||
|
]
|
||||||
|
self.assertEqual(result, [
|
||||||
|
(book1_pk, ct.pk, None),
|
||||||
|
(self.book2.pk, ct.pk, self.book2),
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
class MultiTableInheritanceTest(TestCase):
|
class MultiTableInheritanceTest(TestCase):
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue