From 63aedd7d614f5ab2026fafb46a3f28b6d80d1469 Mon Sep 17 00:00:00 2001 From: etj Date: Thu, 29 Jun 2023 15:48:22 +0200 Subject: [PATCH] [Fixes #11102] Faceting: prefiltering --- geonode/base/api/filters.py | 15 ++++++++++----- geonode/facets/tests.py | 23 +++++++++++++++++++++++ geonode/facets/views.py | 13 +++++++++++-- 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/geonode/base/api/filters.py b/geonode/base/api/filters.py index f8691e0178d..a2428bfdc65 100644 --- a/geonode/base/api/filters.py +++ b/geonode/base/api/filters.py @@ -56,11 +56,16 @@ class TKeywordsFilter(BaseFilterBackend): """ def filter_queryset(self, request, queryset, view): - return ( - self.filter_queryset_GROUP(request, queryset, view) - if "force_and" not in request.GET - else self.filter_queryset_AND(request, queryset, view) - ) + # we must make the GET mutable since in the filters, some queryparams are popped + request.GET._mutable = True + try: + return ( + self.filter_queryset_GROUP(request, queryset, view) + if "force_and" not in request.GET + else self.filter_queryset_AND(request, queryset, view) + ) + finally: + request.GET._mutable = False def filter_queryset_AND(self, request, queryset, view): """ diff --git a/geonode/facets/tests.py b/geonode/facets/tests.py index d582f4e2041..b5ae7f7541f 100644 --- a/geonode/facets/tests.py +++ b/geonode/facets/tests.py @@ -28,6 +28,8 @@ from django.urls import reverse from geonode.base.models import Thesaurus, ThesaurusLabel, ThesaurusKeyword, ThesaurusKeywordLabel, ResourceBase, Region +from geonode.facets.models import facet_registry +from geonode.facets.providers.region import RegionFacetProvider from geonode.tests.base import GeoNodeBaseTestSupport import geonode.facets.views as views @@ -294,6 +296,27 @@ def test_topics(self): obj = json.loads(res.content) self.assertEqual(exp, len(obj["topics"]["items"]), f"Unexpected topic count {exp} for facet {facet}") + def test_prefiltering(self): + reginfo = RegionFacetProvider().get_info() + t0info = facet_registry.get_provider("t_0").get_info() + t1info = facet_registry.get_provider("t_1").get_info() + + for facet, filters, totals, count0 in ( + ("t_0", {}, 2, 10), + ("t_0", {reginfo["key"]: "R0"}, 1, 1), + ("t_1", {}, 2, 10), + ("t_1", {reginfo["key"]: "R0"}, 1, 2), + ("t_1", {reginfo["key"]: "R1"}, 2, 3), + (reginfo["name"], {}, 2, 4), + (reginfo["name"], {t0info["key"]: self.thesauri_k["0_0"].id}, 2, 1), + (reginfo["name"], {t1info["key"]: self.thesauri_k["1_0"].id}, 2, 3), + ): + req = self.rf.get(reverse("get_facet", args=[facet]), data=filters) + res: JsonResponse = views.get_facet(req, facet) + obj = json.loads(res.content) + self.assertEqual(totals, obj["topics"]["total"], f"Bad totals for facet '{facet} and filter {filters}") + self.assertEqual(count0, obj["topics"]["items"][0]["count"], f"Bad count0 for facet '{facet}") + def test_user_auth(self): # make sure the user authorization pre-filters the visible resources # TODO test diff --git a/geonode/facets/views.py b/geonode/facets/views.py index 8aa18439f83..717eabfc57a 100644 --- a/geonode/facets/views.py +++ b/geonode/facets/views.py @@ -27,6 +27,8 @@ from django.urls import reverse from django.conf import settings + +from geonode.base.api.views import ResourceBaseViewSet from geonode.base.models import ResourceBase from geonode.facets.models import FacetProvider, DEFAULT_FACET_PAGE_SIZE, facet_registry from geonode.security.utils import get_visible_resources @@ -160,8 +162,15 @@ def _prefilter_topics(request): :return: a QuerySet on ResourceBase """ logger.debug("Filtering by user '%s'", request.user) - # return ResourceBase.objects - return get_visible_resources(ResourceBase.objects, request.user) + filters = {k: vlist for k, vlist in request.query_params.lists() if k.startswith("filter{")} + + if filters: + viewset = ResourceBaseViewSet(request=request, format_kwarg={}, kwargs=filters) + viewset.initial(request) + return get_visible_resources(queryset=viewset.filter_queryset(viewset.get_queryset()), user=request.user) + else: + # return ResourceBase.objects + return get_visible_resources(ResourceBase.objects, request.user) def _resolve_language(request) -> (str, bool):