Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow diacritic-insensitive searching in admin list views for People/Places (#1479) #1576

Merged
merged 1 commit into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions geniza/entities/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from django.contrib import admin, messages
from django.contrib.contenttypes.admin import GenericTabularInline
from django.contrib.contenttypes.forms import BaseGenericInlineFormSet
from django.contrib.postgres.aggregates import ArrayAgg
from django.db.models.fields import CharField, TextField
from django.forms import ModelChoiceField, ValidationError
from django.forms.models import ModelChoiceIterator
Expand Down Expand Up @@ -250,7 +251,7 @@ def get_formset(self, request, obj=None, **kwargs):
class PersonAdmin(TabbedTranslationAdmin, SortableAdminBase, admin.ModelAdmin):
"""Admin for Person entities in the PGP"""

search_fields = ("names__name",)
search_fields = ("name_unaccented", "names__name")
fields = ("gender", "role", "has_page", "description")
inlines = (
NameInline,
Expand Down Expand Up @@ -285,7 +286,15 @@ def get_form(self, request, obj=None, **kwargs):
def get_queryset(self, request):
"""For autocomplete ONLY, remove self from queryset, so that Person-Person autocomplete
does not include self in the list of options"""
qs = super().get_queryset(request)
# also add unaccented name to queryset so we can search on it
qs = (
super()
.get_queryset(request)
.annotate(
# ArrayAgg to group together related values from related model instances
name_unaccented=ArrayAgg("names__name__unaccent", distinct=True),
)
)

# only modify if this is the person-person autocomplete request
is_autocomplete = request and request.path == "/admin/autocomplete/"
Expand Down Expand Up @@ -448,7 +457,7 @@ def get_formset(self, request, obj=None, **kwargs):
class PlaceAdmin(SortableAdminBase, admin.ModelAdmin):
"""Admin for Place entities in the PGP"""

search_fields = ("names__name",)
search_fields = ("name_unaccented", "names__name")
fields = (("latitude", "longitude"), "notes")
inlines = (
NameInline,
Expand All @@ -470,6 +479,18 @@ class PlaceAdmin(SortableAdminBase, admin.ModelAdmin):
"i", # FootnoteInline
)

def get_queryset(self, request):
"""Modify queryset to add unaccented name annotation field, so that places
can be searched from admin list view without entering diacritics"""
return (
super()
.get_queryset(request)
.annotate(
# ArrayAgg to group together related values from related model instances
name_unaccented=ArrayAgg("names__name__unaccent", distinct=True),
)
)


@admin.register(PlacePlaceRelationType)
class PlacePlaceRelationTypeAdmin(TabbedTranslationAdmin, admin.ModelAdmin):
Expand Down
14 changes: 14 additions & 0 deletions geniza/entities/tests/test_entities_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
PersonPersonRelationTypeChoiceField,
PersonPersonReverseInline,
PersonPlaceInline,
PlaceAdmin,
)
from geniza.entities.models import (
Name,
Expand Down Expand Up @@ -290,3 +291,16 @@ def test_get_min_num(self, admin_client, document):
)
content = str(response.content)
assert 'name="documenteventrelation_set-MIN_NUM_FORMS" value="0"' in content


@pytest.mark.django_db
class TestPlaceAdmin:
def test_get_queryset(self):
# create a place
place = Place.objects.create()
Name.objects.create(name="Fusṭāṭ", content_object=place, primary=True)
place_admin = PlaceAdmin(Place, admin_site=admin.site)

# queryset should include name_unaccented field without diacritics
qs = place_admin.get_queryset(Mock())
assert qs.filter(name_unaccented__icontains="fustat").exists()
11 changes: 11 additions & 0 deletions geniza/entities/tests/test_entities_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,12 @@ def test_get_queryset(self):
assert qs.count() == 1
assert qs.first().pk == person.pk

# should allow search by name WITH diacritics
person_autocomplete_view.request.GET = {"q": "Ḥayyim"}
qs = person_autocomplete_view.get_queryset()
assert qs.count() == 1
assert qs.first().pk == person_2.pk


class TestPlaceAutocompleteView:
@pytest.mark.django_db
Expand All @@ -135,6 +141,11 @@ def test_get_queryset(self):

# should filter on place name, case and diacritic insensitive
place_autocomplete_view.request = Mock()
place_autocomplete_view.request.GET = {"q": "Fusṭāṭ"}
qs = place_autocomplete_view.get_queryset()
assert qs.count() == 1
assert qs.first().pk == place.pk

place_autocomplete_view.request.GET = {"q": "fustat"}
qs = place_autocomplete_view.get_queryset()
assert qs.count() == 1
Expand Down
5 changes: 4 additions & 1 deletion geniza/entities/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.contrib.postgres.aggregates import ArrayAgg
from django.db.models import Q
from django.forms import ValidationError
from django.http import HttpResponseRedirect
from django.urls import reverse
Expand Down Expand Up @@ -85,7 +86,9 @@ def get_queryset(self):
name_unaccented=ArrayAgg("names__name__unaccent", distinct=True),
).order_by("name_unaccented")
if q:
qs = qs.filter(name_unaccented__icontains=q)
qs = qs.filter(
Q(name_unaccented__icontains=q) | Q(names__name__icontains=q)
).distinct()
return qs


Expand Down
Loading