Use subquery to remove duplicates in SearchFilter

This commit is contained in:
sevdog 2023-06-22 10:39:52 +02:00
parent 4f7e9ed3bb
commit dee83cebf4
No known key found for this signature in database
GPG Key ID: D939AF7A93A9C178
2 changed files with 7 additions and 14 deletions

View File

@ -3,7 +3,6 @@ The `compat` module provides support for backwards compatibility with older
versions of Django/Python, and compatibility wrappers around optional packages.
"""
import django
from django.conf import settings
from django.views.generic import View
@ -14,13 +13,6 @@ def unicode_http_header(value):
return value
def distinct(queryset, base):
if settings.DATABASES[queryset.db]["ENGINE"] == "django.db.backends.oracle":
# distinct analogue for Oracle users
return base.filter(pk__in=set(queryset.values_list('pk', flat=True)))
return queryset.distinct()
# django.contrib.postgres requires psycopg2
try:
from django.contrib.postgres import fields as postgres_fields

View File

@ -14,7 +14,7 @@ from django.utils.encoding import force_str
from django.utils.translation import gettext_lazy as _
from rest_framework import RemovedInDRF317Warning
from rest_framework.compat import coreapi, coreschema, distinct
from rest_framework.compat import coreapi, coreschema
from rest_framework.settings import api_settings
@ -127,12 +127,13 @@ class SearchFilter(BaseFilterBackend):
conditions.append(reduce(operator.or_, queries))
queryset = queryset.filter(reduce(operator.and_, conditions))
# Remove duplicates from results, if necessary
if self.must_call_distinct(queryset, search_fields):
# Filtering against a many-to-many field requires us to
# call queryset.distinct() in order to avoid duplicate items
# in the resulting queryset.
# We try to avoid this if possible, for performance reasons.
queryset = distinct(queryset, base)
# inspired by django.contrib.admin
# this is more accurate than .distinct form M2M relationship
# also is cross-database
queryset = queryset.filter(pk=models.OuterRef('pk'))
queryset = base.filter(models.Exists(queryset))
return queryset
def to_html(self, request, queryset, view):