From 0534f7400237720fff03ac8a5f8bcd85e5176a72 Mon Sep 17 00:00:00 2001 From: Matt Westcott Date: Mon, 7 Nov 2022 16:29:49 +0000 Subject: [PATCH] Fix handling of generic foreign keys in reference index Fix two bugs with the handling of generic foreign keys within `ReferenceIndex._extract_references_from_object`: * null values were not considered * content type ID was copied directly from the source field, unlike all other cases that translate to the base content type instead - lookups use the base content type, so these results were not returned. Fixes #9615 --- wagtail/models/reference_index.py | 11 +++++-- ...t_genericsnippetpage_content_type_blank.py | 30 +++++++++++++++++++ wagtail/test/testapp/models.py | 4 +-- wagtail/tests/test_reference_index.py | 14 +++++++++ 4 files changed, 54 insertions(+), 5 deletions(-) create mode 100644 wagtail/test/testapp/migrations/0012_set_genericsnippetpage_content_type_blank.py diff --git a/wagtail/models/reference_index.py b/wagtail/models/reference_index.py index 7d857c0830..1618d238d4 100644 --- a/wagtail/models/reference_index.py +++ b/wagtail/models/reference_index.py @@ -265,10 +265,15 @@ class ReferenceIndex(models.Model): if isinstance(field, GenericForeignKey): ct_field = object._meta.get_field(field.ct_field) fk_field = object._meta.get_field(field.fk_field) + ct_value = ct_field.value_from_object(object) + fk_value = fk_field.value_from_object(object) + + if ct_value is not None and fk_value is not None: + model = ContentType.objects.get_for_id(ct_value).model_class() + yield cls._get_base_content_type(model).id, str( + fk_value + ), field.name, field.name - yield ct_field.value_from_object(object), str( - fk_field.value_from_object(object) - ), field.name, field.name continue if isinstance(field, GenericRel): diff --git a/wagtail/test/testapp/migrations/0012_set_genericsnippetpage_content_type_blank.py b/wagtail/test/testapp/migrations/0012_set_genericsnippetpage_content_type_blank.py new file mode 100644 index 0000000000..3302c8971a --- /dev/null +++ b/wagtail/test/testapp/migrations/0012_set_genericsnippetpage_content_type_blank.py @@ -0,0 +1,30 @@ +# Generated by Django 4.1.2 on 2022-11-07 16:23 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("contenttypes", "0002_remove_content_type_name"), + ("tests", "0011_modelwithnullableparentalkey"), + ] + + operations = [ + migrations.AlterField( + model_name="genericsnippetpage", + name="snippet_content_type", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="contenttypes.contenttype", + ), + ), + migrations.AlterField( + model_name="genericsnippetpage", + name="snippet_object_id", + field=models.PositiveIntegerField(blank=True, null=True), + ), + ] diff --git a/wagtail/test/testapp/models.py b/wagtail/test/testapp/models.py index 1f243b3355..7efa2d0543 100644 --- a/wagtail/test/testapp/models.py +++ b/wagtail/test/testapp/models.py @@ -1567,9 +1567,9 @@ class GenericSnippetPage(Page): """ snippet_content_type = models.ForeignKey( - ContentType, on_delete=models.SET_NULL, null=True + ContentType, on_delete=models.SET_NULL, null=True, blank=True ) - snippet_object_id = models.PositiveIntegerField(null=True) + snippet_object_id = models.PositiveIntegerField(null=True, blank=True) snippet_content_object = GenericForeignKey( "snippet_content_type", "snippet_object_id" ) diff --git a/wagtail/tests/test_reference_index.py b/wagtail/tests/test_reference_index.py index 30089d0830..853ee54b14 100644 --- a/wagtail/tests/test_reference_index.py +++ b/wagtail/tests/test_reference_index.py @@ -10,6 +10,7 @@ from wagtail.models import Page, ReferenceIndex from wagtail.test.testapp.models import ( EventPage, EventPageCarouselItem, + GenericSnippetPage, ModelWithNullableParentalKey, ) @@ -179,6 +180,19 @@ class TestCreateOrUpdateForObject(TestCase): refs = ReferenceIndex.get_references_to(self.event_page) self.assertEqual(refs.count(), 0) + def test_generic_foreign_key(self): + page1 = GenericSnippetPage( + title="generic snippet page", snippet_content_object=self.event_page + ) + self.root_page.add_child(instance=page1) + page2 = GenericSnippetPage( + title="generic snippet page", snippet_content_object=None + ) + self.root_page.add_child(instance=page2) + + refs = ReferenceIndex.get_references_to(self.event_page) + self.assertEqual(refs.count(), 1) + def test_rebuild_references_index_no_verbosity(self): stdout = StringIO() management.call_command(