diff --git a/readthedocs/projects/filters.py b/readthedocs/projects/filters.py index c3495c0beca..f9a9897eca9 100644 --- a/readthedocs/projects/filters.py +++ b/readthedocs/projects/filters.py @@ -4,9 +4,7 @@ from django.db.models import Count from django.db.models import F from django.db.models import Max -from django.db.models import Q from django.utils.translation import gettext_lazy as _ -from django_filters import CharFilter from django_filters import ChoiceFilter from django_filters import OrderingFilter @@ -261,12 +259,20 @@ class RedirectListFilterSet(ModelFilterSet): empty_label=_("All types"), ) - # Matches against either end of the redirect in a single field so the UI - # stays a single input. - url = CharFilter( - label=_("URL contains"), - method="filter_url_contains", + url = FilteredModelChoiceFilter( + label=_("URL"), + empty_label=_("All URLs"), + to_field_name="from_url", + queryset_method="get_redirect_queryset", + method="get_redirect", + label_attribute="from_url", ) - def filter_url_contains(self, queryset, field_name, value): - return queryset.filter(Q(from_url__icontains=value) | Q(to_url__icontains=value)) + def get_redirect_queryset(self): + # Scope choices to the project's redirects passed in at instantiation; + # otherwise the dropdown would show every Redirect across all projects. + # Exclude redirects without a ``from_url`` (eg. clean URL <-> HTML types). + return self.queryset.exclude(from_url="") + + def get_redirect(self, queryset, field_name, redirect): + return queryset.filter(from_url=redirect.from_url) diff --git a/readthedocs/redirects/tests/test_views.py b/readthedocs/redirects/tests/test_views.py index 1e33469a1dc..a16f8923be3 100644 --- a/readthedocs/redirects/tests/test_views.py +++ b/readthedocs/redirects/tests/test_views.py @@ -98,8 +98,8 @@ def test_list_redirect_filter_by_type(self): assert len(resp.context["redirects"]) == 1 assert resp.context["redirects"][0].redirect_type == PAGE_REDIRECT - def test_list_redirect_filter_by_url_contains(self): - get( + def test_list_redirect_filter_by_url(self): + other = get( Redirect, project=self.project, redirect_type=PAGE_REDIRECT, @@ -108,13 +108,11 @@ def test_list_redirect_filter_by_url_contains(self): ) resp = self.client.get( reverse("projects_redirects", args=[self.project.slug]), - {"url": "configuration"}, + {"url": other.from_url}, ) assert resp.status_code == 200 pks = {r.pk for r in resp.context["redirects"]} - assert len(pks) == 1 - # Matches on the ``to_url`` substring only. - assert self.redirect.pk not in pks + assert pks == {other.pk} def test_get_redirect(self): resp = self.client.get(