improve event organizer search

This commit is contained in:
2026-07-05 17:53:33 +02:00
parent e57eb48c88
commit 36c01902a1
4 changed files with 78 additions and 2 deletions
+1 -1
View File
@@ -164,7 +164,7 @@ class EventOrganizerLink(Orderable):
] ]
@register_snippet # registered as a snippet via EventOrganizerSnippetViewSet in wagtail_hooks.py
@register_query_field("eventOrganizer", "eventOrganizers") @register_query_field("eventOrganizer", "eventOrganizers")
class EventOrganizer(index.Indexed, ClusterableModel): class EventOrganizer(index.Indexed, ClusterableModel):
objects = WPAwareManager() objects = WPAwareManager()
+44
View File
@@ -1,11 +1,53 @@
from django import forms
from django.db.models import Q
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from wagtail.admin.forms import WagtailAdminModelForm from wagtail.admin.forms import WagtailAdminModelForm
from wagtail.admin.forms.choosers import BaseFilterForm
from wagtail.admin.views.generic.chooser import ChooseResultsView, ChooseView
from wagtail.admin.viewsets.chooser import ChooserViewSet from wagtail.admin.viewsets.chooser import ChooserViewSet
from wagtail.snippets.views.snippets import SnippetViewSet
from dnscms.utils import slugify from dnscms.utils import slugify
from events.models import EventOrganizer from events.models import EventOrganizer
class EventOrganizerSearchForm(BaseFilterForm):
"""Substring search on name/slug"""
q = forms.CharField(
label=_("Search term"),
widget=forms.TextInput(attrs={"placeholder": _("Search")}),
required=False,
)
def filter(self, objects):
objects = super().filter(objects)
search_query = self.cleaned_data.get("q")
if search_query:
objects = objects.filter(
Q(name__icontains=search_query) | Q(slug__icontains=search_query)
)
self.is_searching = True
self.search_query = search_query
return objects
class EventOrganizerChooseView(ChooseView):
filter_form_class = EventOrganizerSearchForm
class EventOrganizerChooseResultsView(ChooseResultsView):
filter_form_class = EventOrganizerSearchForm
class EventOrganizerSnippetViewSet(SnippetViewSet):
model = EventOrganizer
icon = "group"
# unset the search backend so the listing search filters on icontains instead
search_backend_name = None
search_fields = ["name", "slug"]
class EventOrganizerCreationForm(WagtailAdminModelForm): class EventOrganizerCreationForm(WagtailAdminModelForm):
class Meta: class Meta:
model = EventOrganizer model = EventOrganizer
@@ -29,6 +71,8 @@ class EventOrganizerChooserViewSet(ChooserViewSet):
choose_another_text = _("Choose another organizer") choose_another_text = _("Choose another organizer")
edit_item_text = _("Edit this organizer") edit_item_text = _("Edit this organizer")
creation_form_class = EventOrganizerCreationForm creation_form_class = EventOrganizerCreationForm
choose_view_class = EventOrganizerChooseView
choose_results_view_class = EventOrganizerChooseResultsView
event_organizer_chooser_viewset = EventOrganizerChooserViewSet("event_organizer_chooser") event_organizer_chooser_viewset = EventOrganizerChooserViewSet("event_organizer_chooser")
+4 -1
View File
@@ -3,10 +3,13 @@ from django.utils.translation import gettext_lazy as _
from wagtail import hooks from wagtail import hooks
from wagtail.admin.auth import user_passes_test from wagtail.admin.auth import user_passes_test
from wagtail.admin.menu import MenuItem from wagtail.admin.menu import MenuItem
from wagtail.snippets.models import register_snippet
from .admin import event_sidebar_viewset, event_explorer_viewset from .admin import event_sidebar_viewset, event_explorer_viewset
from .merge_organizers import MergeOrganizersConfirmView, MergeOrganizersSelectView from .merge_organizers import MergeOrganizersConfirmView, MergeOrganizersSelectView
from .views import event_organizer_chooser_viewset from .views import EventOrganizerSnippetViewSet, event_organizer_chooser_viewset
register_snippet(EventOrganizerSnippetViewSet)
superuser_only = user_passes_test(lambda user: user.is_superuser) superuser_only = user_passes_test(lambda user: user.is_superuser)
+29
View File
@@ -4,6 +4,7 @@ import pytest
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import connection from django.db import connection
from django.test.utils import CaptureQueriesContext from django.test.utils import CaptureQueriesContext
from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from events.admin import EventDateColumn, OrganizersColumn from events.admin import EventDateColumn, OrganizersColumn
@@ -572,3 +573,31 @@ def test_graphql_event_index_returns_all_fields_for_comprehensive_event(
assert datetime.fromisoformat(venue_occ["end"]) == venue_occ_db.end assert datetime.fromisoformat(venue_occ["end"]) == venue_occ_db.end
assert datetime.fromisoformat(custom_occ["start"]) == custom_occ_db.start assert datetime.fromisoformat(custom_occ["start"]) == custom_occ_db.start
assert datetime.fromisoformat(custom_occ["end"]) == custom_occ_db.end assert datetime.fromisoformat(custom_occ["end"]) == custom_occ_db.end
@pytest.fixture
def searchable_organizers(db):
return [
EventOrganizer.objects.create(name="Arrangementsutvalget", slug="arrangementsutvalget"),
EventOrganizer.objects.create(name="Cinema Neuf", slug="kino"),
]
@pytest.mark.parametrize(
"url_name",
["event_organizer_chooser:choose", "wagtailsnippets_events_eventorganizer:list"],
)
def test_organizer_search_matches_substrings_of_name_and_slug(
admin_client, searchable_organizers, url_name
):
url = reverse(url_name)
# mid-word match inside a compound name
content = admin_client.get(url, {"q": "utvalget"}).content.decode()
assert "Arrangementsutvalget" in content
assert "Cinema Neuf" not in content
# slug-only match
content = admin_client.get(url, {"q": "kino"}).content.decode()
assert "Cinema Neuf" in content
assert "Arrangementsutvalget" not in content