Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix DISTINCT for Oracle databases #2935

Closed
wants to merge 6 commits into from
Closed
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions rest_framework/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
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
Expand Down Expand Up @@ -100,12 +101,19 @@ def filter_queryset(self, request, queryset, view):

orm_lookups = [self.construct_search(six.text_type(search_field))
for search_field in search_fields]

and_queries = []
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I understand the motivation here - feels more obscure after this change rather than more clear.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't sure, if calling queryset.filter(pk_in=set(pk_list)) will make duplications go away, as they pk still will be in pk_list. I want to filter original queryset by pk_list.
When we call filter with multiple kwargs or several times on one queryset it is equal to logical and.

for search_term in self.get_search_terms(request):
or_queries = [models.Q(**{orm_lookup: search_term})
for orm_lookup in orm_lookups]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer it if we didn't introduce the and_queries. We should keep each pull request as absolutely minimal as possible. Seems like there's two different changes here - the Oracle support as one, and the change in the filtering style as another. Let's just adopt the first of those two.
Apologies for the slow progress on this, but thanks for getting it nearly there! :)

queryset = queryset.filter(reduce(operator.or_, or_queries)).distinct()

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":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll want a brief comment here regarding needing this behavior for oracle and distinct.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tomchristie According to Oracle DB limits there is no capability to make a DISTINT on *LOB. There is a need to use SearchFilter with TextField (for example). That's why i pull this workaround for Oracle.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clarification: I was meaning that we should have a code comment for this.

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


Expand Down