From d737f29a629da8d2b018fa8d734c7c45a2754d3f Mon Sep 17 00:00:00 2001 From: varun kumar Date: Wed, 4 Oct 2023 21:58:15 +0530 Subject: [PATCH] Use logical OR operator to combine search fields for Django ORM in IndexView --- CHANGELOG.txt | 1 + CONTRIBUTORS.md | 1 + docs/releases/5.2.1.md | 1 + .../tests/viewsets/test_model_viewset.py | 39 +++++++++++++++++++ wagtail/admin/views/generic/models.py | 11 +++--- .../migrations/0031_searchtestmodel.py | 29 ++++++++++++++ wagtail/test/testapp/models.py | 12 ++++++ wagtail/test/testapp/views.py | 7 ++++ wagtail/test/testapp/wagtail_hooks.py | 7 +++- 9 files changed, 101 insertions(+), 7 deletions(-) create mode 100644 wagtail/test/testapp/migrations/0031_searchtestmodel.py diff --git a/CHANGELOG.txt b/CHANGELOG.txt index c4339a35fb..8001423f86 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -32,6 +32,7 @@ Changelog * Fix: Add a fallback background for the editing preview iframe for sites without a background (Ian Price) * Fix: Remove search logging from project template so that new projects without the search promotions module will not error (Matt Westcott) * Fix: Ensure text only email notifications for updated comments do not escape HTML characters (Rohit Sharma) + * Fix: Use logical OR operator to combine search fields for Django ORM in generic IndexView (Varun Kumar) * Docs: Fix code example for `{% picture ... as ... %}` template tag (Rezyapkin) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index cb2e6dfe93..7d6a6c5888 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -763,6 +763,7 @@ * Marcel Kornblum * Cameron Lamb * Sam Dudley +* Varun Kumar ## Translators diff --git a/docs/releases/5.2.1.md b/docs/releases/5.2.1.md index 3fe0622cac..9166061546 100644 --- a/docs/releases/5.2.1.md +++ b/docs/releases/5.2.1.md @@ -17,6 +17,7 @@ depth: 1 * Add a fallback background for the editing preview iframe for sites without a background (Ian Price) * Remove search logging from project template so that new projects without the search promotions module will not error (Matt Westcott) * Ensure text only email notifications for updated comments do not escape HTML characters (Rohit Sharma) + * Use logical OR operator to combine search fields for Django ORM in generic IndexView (Varun Kumar) ### Documentation diff --git a/wagtail/admin/tests/viewsets/test_model_viewset.py b/wagtail/admin/tests/viewsets/test_model_viewset.py index b194c21de0..d4ae29d9a6 100644 --- a/wagtail/admin/tests/viewsets/test_model_viewset.py +++ b/wagtail/admin/tests/viewsets/test_model_viewset.py @@ -17,6 +17,7 @@ from wagtail.models import ModelLogEntry from wagtail.test.testapp.models import ( FeatureCompleteToy, JSONStreamModel, + SearchTestModel, VariousOnDeleteModel, ) from wagtail.test.utils.template_tests import AdminTemplateTestUtils @@ -452,6 +453,44 @@ class TestSearchIndexResultsView(TestSearchIndexView): pass +class TestSearchFields(WagtailTestUtils, TestCase): + @classmethod + def setUpTestData(cls): + objects = [ + SearchTestModel(title="Hello World", body="This one is classic"), + SearchTestModel(title="Hello Anime", body="We love anime (opinions vary)"), + SearchTestModel(title="Food", body="I like food, do you?"), + ] + SearchTestModel.objects.bulk_create(objects) + + def setUp(self): + self.login() + + def get(self, q): + return self.client.get(reverse("searchtest:index"), {"q": q}) + + def test_single_result_with_body(self): + response = self.get("IkE") + self.assertEqual(response.status_code, 200) + self.assertNotContains(response, "Hello World") + self.assertNotContains(response, "Hello Anime") + self.assertContains(response, "Food") + + def test_multiple_results_with_title(self): + response = self.get("ELlo") + self.assertEqual(response.status_code, 200) + self.assertContains(response, "Hello World") + self.assertContains(response, "Hello Anime") + self.assertNotContains(response, "Food") + + def test_no_results(self): + response = self.get("Abra Kadabra") + self.assertEqual(response.status_code, 200) + self.assertNotContains(response, "Hello World") + self.assertNotContains(response, "Hello Anime") + self.assertNotContains(response, "Food") + + class TestListExport(WagtailTestUtils, TestCase): def setUp(self): self.user = self.login() diff --git a/wagtail/admin/views/generic/models.py b/wagtail/admin/views/generic/models.py index e347834442..4c792c75fe 100644 --- a/wagtail/admin/views/generic/models.py +++ b/wagtail/admin/views/generic/models.py @@ -9,6 +9,7 @@ from django.core.exceptions import ( PermissionDenied, ) from django.db import models, transaction +from django.db.models import Q from django.db.models.functions import Cast from django.forms import Form from django.http import Http404, HttpResponseRedirect @@ -295,12 +296,10 @@ class IndexView( return search_backend.search( self.search_query, queryset, fields=self.search_fields ) - - filters = { - field + "__icontains": self.search_query - for field in self.search_fields or [] - } - return queryset.filter(**filters) + query = Q() + for field in self.search_fields or []: + query |= Q(**{field + "__icontains": self.search_query}) + return queryset.filter(query) def _get_title_column(self, field_name, column_class=TitleColumn, **kwargs): if not issubclass(column_class, ButtonsColumnMixin): diff --git a/wagtail/test/testapp/migrations/0031_searchtestmodel.py b/wagtail/test/testapp/migrations/0031_searchtestmodel.py new file mode 100644 index 0000000000..b77aabb5ba --- /dev/null +++ b/wagtail/test/testapp/migrations/0031_searchtestmodel.py @@ -0,0 +1,29 @@ +# Generated by Django 4.2.7 on 2023-11-08 14:46 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("tests", "0030_purgerevisionsprotectedtestmodel"), + ] + + operations = [ + migrations.CreateModel( + name="SearchTestModel", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("title", models.CharField(max_length=255)), + ("body", models.TextField()), + ], + ), + ] diff --git a/wagtail/test/testapp/models.py b/wagtail/test/testapp/models.py index c3cef9b4d1..2e358aa7b6 100644 --- a/wagtail/test/testapp/models.py +++ b/wagtail/test/testapp/models.py @@ -2177,3 +2177,15 @@ class PurgeRevisionsProtectedTestModel(models.Model): revision = models.OneToOneField( "wagtailcore.Revision", on_delete=models.PROTECT, related_name="+" ) + + +class SearchTestModel(models.Model): + title = models.CharField(max_length=255) + body = models.TextField() + panels = [ + FieldPanel("title"), + FieldPanel("body"), + ] + + def __str__(self): + return self.title diff --git a/wagtail/test/testapp/views.py b/wagtail/test/testapp/views.py index 0a3f322b76..89379b9843 100644 --- a/wagtail/test/testapp/views.py +++ b/wagtail/test/testapp/views.py @@ -24,6 +24,7 @@ from wagtail.test.testapp.models import ( JSONMinMaxCountStreamModel, JSONStreamModel, ModelWithStringTypePrimaryKey, + SearchTestModel, ) @@ -196,6 +197,12 @@ class JSONModelViewSetGroup(ModelViewSetGroup): ) +class SearchTestModelViewSet(ModelViewSet): + model = SearchTestModel + search_fields = ["title", "body"] + form_fields = ["title", "body"] + + class FeatureCompleteToyViewSet(ModelViewSet): model = FeatureCompleteToy url_namespace = "feature_complete_toy" diff --git a/wagtail/test/testapp/wagtail_hooks.py b/wagtail/test/testapp/wagtail_hooks.py index 83cdca3b57..717fae2e3c 100644 --- a/wagtail/test/testapp/wagtail_hooks.py +++ b/wagtail/test/testapp/wagtail_hooks.py @@ -35,6 +35,7 @@ from wagtail.test.testapp.models import ( from wagtail.test.testapp.views import ( JSONModelViewSetGroup, MiscellaneousViewSetGroup, + SearchTestModelViewSet, ToyViewSetGroup, animated_advert_chooser_viewset, ) @@ -242,7 +243,11 @@ def add_broken_links_summary_item(request, items): @hooks.register("register_admin_viewset") def register_viewsets(): - return [MiscellaneousViewSetGroup(), JSONModelViewSetGroup()] + return [ + MiscellaneousViewSetGroup(), + JSONModelViewSetGroup(), + SearchTestModelViewSet(name="searchtest"), + ] @hooks.register("register_admin_viewset")