Skip to content

Commit

Permalink
Clean-up refactoring of SearchFilter implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
tomchristie committed Aug 20, 2015
1 parent aa4cd7e commit b4b2dc1
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 18 deletions.
7 changes: 7 additions & 0 deletions rest_framework/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ def total_seconds(timedelta):
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.
# This will always be the case in Django 1.7 and above, as these versions
# no longer support Python 2.6.
Expand Down
39 changes: 21 additions & 18 deletions rest_framework/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@
import operator
from functools import reduce

from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.db import models
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

FilterSet = django_filters and django_filters.FilterSet or None
Expand Down Expand Up @@ -99,25 +100,27 @@ def construct_search(self, field_name):
def filter_queryset(self, request, queryset, view):
search_fields = getattr(view, 'search_fields', None)

if not search_fields:
return queryset

original_queryset = queryset
orm_lookups = [self.construct_search(six.text_type(search_field))
for search_field in 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)

for search_term in self.get_search_terms(request):
or_queries = [models.Q(**{orm_lookup: search_term})
for orm_lookup in orm_lookups]
queryset = queryset.filter(reduce(operator.or_, or_queries))
if not search_fields or not search_terms:
return queryset

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()
base = queryset
for search_term in search_terms:
queries = [
models.Q(**{orm_lookup: search_term})
for orm_lookup in orm_lookups
]
queryset = queryset.filter(reduce(operator.or_, queries))

return queryset
# Filtering against a many-to-many field requires us to
# call queryset.distinct() in order to avoid duplicate items
# in the resulting queryset.
return distinct(queryset, base)


class OrderingFilter(BaseFilterBackend):
Expand Down

0 comments on commit b4b2dc1

Please sign in to comment.