Clean-up refactoring of SearchFilter implementation

This commit is contained in:
Tom Christie 2015-08-20 11:35:32 +01:00
parent aa4cd7e9d7
commit b4b2dc18fa
2 changed files with 28 additions and 18 deletions

View File

@ -50,6 +50,13 @@ def total_seconds(timedelta):
return (timedelta.days * 86400.0) + float(timedelta.seconds) + (timedelta.microseconds / 1000000.0) return (timedelta.days * 86400.0) + float(timedelta.seconds) + (timedelta.microseconds / 1000000.0)
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()
# OrderedDict only available in Python 2.7. # OrderedDict only available in Python 2.7.
# This will always be the case in Django 1.7 and above, as these versions # This will always be the case in Django 1.7 and above, as these versions
# no longer support Python 2.6. # no longer support Python 2.6.

View File

@ -7,12 +7,13 @@ from __future__ import unicode_literals
import operator import operator
from functools import reduce from functools import reduce
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.db import models from django.db import models
from django.utils import six from django.utils import six
from rest_framework.compat import django_filters, get_model_name, guardian from rest_framework.compat import (
distinct, django_filters, get_model_name, guardian
)
from rest_framework.settings import api_settings from rest_framework.settings import api_settings
FilterSet = django_filters and django_filters.FilterSet or None FilterSet = django_filters and django_filters.FilterSet or None
@ -99,25 +100,27 @@ class SearchFilter(BaseFilterBackend):
def filter_queryset(self, request, queryset, view): def filter_queryset(self, request, queryset, view):
search_fields = getattr(view, 'search_fields', None) search_fields = getattr(view, 'search_fields', None)
if not search_fields: orm_lookups = [
self.construct_search(six.text_type(search_field))
for search_field in search_fields
]
search_terms = self.get_search_terms(request)
if not search_fields or not search_terms:
return queryset return queryset
original_queryset = queryset base = queryset
orm_lookups = [self.construct_search(six.text_type(search_field)) for search_term in search_terms:
for search_field in search_fields] queries = [
models.Q(**{orm_lookup: search_term})
for orm_lookup in orm_lookups
]
queryset = queryset.filter(reduce(operator.or_, queries))
for search_term in self.get_search_terms(request): # Filtering against a many-to-many field requires us to
or_queries = [models.Q(**{orm_lookup: search_term}) # call queryset.distinct() in order to avoid duplicate items
for orm_lookup in orm_lookups] # in the resulting queryset.
queryset = queryset.filter(reduce(operator.or_, or_queries)) return distinct(queryset, base)
if settings.DATABASES[queryset.db]["ENGINE"] == "django.db.backends.oracle":
# distinct analogue for Oracle users
queryset = original_queryset.filter(pk__in=set(queryset.values_list('pk', flat=True)))
else:
queryset = queryset.distinct()
return queryset
class OrderingFilter(BaseFilterBackend): class OrderingFilter(BaseFilterBackend):