From 7813d2fb35cb636116186bddcb58644684ee2723 Mon Sep 17 00:00:00 2001 From: tony Date: Thu, 14 May 2015 13:57:36 +0300 Subject: [PATCH 1/6] fix DISTINCT for Oracle databases --- rest_framework/filters.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rest_framework/filters.py b/rest_framework/filters.py index 9a84efa23..0dea2434b 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -7,6 +7,7 @@ from __future__ import unicode_literals from django.core.exceptions import ImproperlyConfigured from django.db import models from django.utils import six +from django.conf import settings from rest_framework.compat import django_filters, guardian, get_model_name from rest_framework.settings import api_settings from functools import reduce @@ -104,7 +105,9 @@ class SearchFilter(BaseFilterBackend): 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)).distinct() + queryset = queryset.filter(reduce(operator.or_, or_queries)) + if settings.DATABASES[queryset.db]["ENGINE"] != "django.db.backends.oracle": + queryset = queryset.distinct() return queryset From c47ec60ea21907a29746e310ec1bc9ab124f9d5a Mon Sep 17 00:00:00 2001 From: tony Date: Thu, 14 May 2015 17:04:22 +0300 Subject: [PATCH 2/6] no need to do distinct on every loop cycle & add analogue of distinct for oracle users --- rest_framework/filters.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/rest_framework/filters.py b/rest_framework/filters.py index 0dea2434b..743469f8f 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -106,9 +106,12 @@ class SearchFilter(BaseFilterBackend): or_queries = [models.Q(**{orm_lookup: search_term}) for orm_lookup in orm_lookups] queryset = queryset.filter(reduce(operator.or_, or_queries)) - if settings.DATABASES[queryset.db]["ENGINE"] != "django.db.backends.oracle": - queryset = queryset.distinct() + if settings.DATABASES[queryset.db]["ENGINE"] != "django.db.backends.oracle": + queryset = queryset.distinct() + else: + pk_list = queryset.values_list('pk', flat=True) + queryset = view.model.objects.filter(pk__in=set(pk_list)) return queryset From de95598a1ed7f1b609b9dbc6415655be65fae4a8 Mon Sep 17 00:00:00 2001 From: tony Date: Fri, 15 May 2015 13:46:56 +0300 Subject: [PATCH 3/6] removed using `view.model.objects` --- rest_framework/filters.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/rest_framework/filters.py b/rest_framework/filters.py index 743469f8f..1ef4c7fa0 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -101,18 +101,17 @@ class SearchFilter(BaseFilterBackend): orm_lookups = [self.construct_search(six.text_type(search_field)) for search_field in search_fields] - + and_queries = [] 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)) + and_queries.append(reduce(operator.or_, or_queries)) - if settings.DATABASES[queryset.db]["ENGINE"] != "django.db.backends.oracle": - queryset = queryset.distinct() + if settings.DATABASES[queryset.db]["ENGINE"] == "django.db.backends.oracle": + pk_list = queryset.filter(reduce(operator.and_, and_queries)).values_list('pk', flat=True) + return queryset.filter(pk__in=frozenset(pk_list)) else: - pk_list = queryset.values_list('pk', flat=True) - queryset = view.model.objects.filter(pk__in=set(pk_list)) - return queryset + return queryset.filter(reduce(operator.and_, and_queries)).distinct() class OrderingFilter(BaseFilterBackend): From 0906bf2c08e67eb43789ef976a784a4da01a08bc Mon Sep 17 00:00:00 2001 From: tony Date: Fri, 15 May 2015 16:44:12 +0300 Subject: [PATCH 4/6] fix empty and_query --- rest_framework/filters.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/rest_framework/filters.py b/rest_framework/filters.py index 1ef4c7fa0..39de22fa2 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -107,11 +107,13 @@ class SearchFilter(BaseFilterBackend): for orm_lookup in orm_lookups] and_queries.append(reduce(operator.or_, or_queries)) - if settings.DATABASES[queryset.db]["ENGINE"] == "django.db.backends.oracle": - pk_list = queryset.filter(reduce(operator.and_, and_queries)).values_list('pk', flat=True) - return queryset.filter(pk__in=frozenset(pk_list)) - else: - return queryset.filter(reduce(operator.and_, and_queries)).distinct() + if and_queries: + if settings.DATABASES[queryset.db]["ENGINE"] == "django.db.backends.oracle": + pk_list = queryset.filter(reduce(operator.and_, and_queries)).values_list('pk', flat=True) + return queryset.filter(pk__in=frozenset(pk_list)) + else: + return queryset.filter(reduce(operator.and_, and_queries)).distinct() + return queryset class OrderingFilter(BaseFilterBackend): From 745d8d0004af56873bf18ed36a68794f738ee985 Mon Sep 17 00:00:00 2001 From: Shtarev Date: Wed, 3 Jun 2015 09:04:28 +0300 Subject: [PATCH 5/6] added comment --- rest_framework/filters.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rest_framework/filters.py b/rest_framework/filters.py index 39de22fa2..ff0b8e653 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -108,6 +108,7 @@ class SearchFilter(BaseFilterBackend): and_queries.append(reduce(operator.or_, or_queries)) if and_queries: + # According to Oracle DB limits there is no capability to make a DISTINT on *LOB if settings.DATABASES[queryset.db]["ENGINE"] == "django.db.backends.oracle": pk_list = queryset.filter(reduce(operator.and_, and_queries)).values_list('pk', flat=True) return queryset.filter(pk__in=frozenset(pk_list)) From e8b23c47890d110071c2d26105596db6e9b94ac8 Mon Sep 17 00:00:00 2001 From: Shtarev Date: Thu, 25 Jun 2015 21:14:00 +0300 Subject: [PATCH 6/6] thin logic --- rest_framework/filters.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/rest_framework/filters.py b/rest_framework/filters.py index ff0b8e653..06dbba179 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -58,6 +58,7 @@ class DjangoFilterBackend(BaseFilterBackend): class Meta: model = queryset.model fields = filter_fields + return AutoFilterSet return None @@ -98,22 +99,24 @@ class SearchFilter(BaseFilterBackend): if not search_fields: return queryset - + if settings.DATABASES[queryset.db]["ENGINE"] == "django.db.backends.oracle": + # Remember queryset for Oracle db users + original_queryset = queryset orm_lookups = [self.construct_search(six.text_type(search_field)) for search_field in search_fields] - and_queries = [] + for search_term in self.get_search_terms(request): or_queries = [models.Q(**{orm_lookup: search_term}) for orm_lookup in orm_lookups] - and_queries.append(reduce(operator.or_, or_queries)) + queryset = queryset.filter(reduce(operator.or_, or_queries)) + if settings.DATABASES[queryset.db]["ENGINE"] != "django.db.backends.oracle": + # Oracle db don't support distinct on *LOB fields + queryset = queryset.distinct() + + 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))) - if and_queries: - # According to Oracle DB limits there is no capability to make a DISTINT on *LOB - if settings.DATABASES[queryset.db]["ENGINE"] == "django.db.backends.oracle": - pk_list = queryset.filter(reduce(operator.and_, and_queries)).values_list('pk', flat=True) - return queryset.filter(pk__in=frozenset(pk_list)) - else: - return queryset.filter(reduce(operator.and_, and_queries)).distinct() return queryset @@ -160,7 +163,7 @@ class OrderingFilter(BaseFilterBackend): field.source or field_name for field_name, field in serializer_class().fields.items() if not getattr(field, 'write_only', False) - ] + ] elif valid_fields == '__all__': # View explicitly allows filtering on any model field valid_fields = [field.name for field in queryset.model._meta.fields] @@ -182,6 +185,7 @@ class DjangoObjectPermissionsFilter(BaseFilterBackend): A filter backend that limits results to those where the requesting user has read object level permissions. """ + def __init__(self): assert guardian, 'Using DjangoObjectPermissionsFilter, but django-guardian is not installed'