From 38ce0d1d57e22b54ccf8565faf44ce2c7ffdea56 Mon Sep 17 00:00:00 2001 From: Anatoly Ivanov Date: Thu, 25 Jan 2018 16:14:45 +0400 Subject: [PATCH 1/3] Fix filterset behavior on multiple negative conditions --- url_filter/backends/django.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/url_filter/backends/django.py b/url_filter/backends/django.py index 4e3b89d..dfa3560 100644 --- a/url_filter/backends/django.py +++ b/url_filter/backends/django.py @@ -1,7 +1,11 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals +import operator +from functools import reduce + from django.db.models.constants import LOOKUP_SEP +from django.db.models import Q from .base import BaseFilterBackend @@ -97,6 +101,7 @@ def filter_by_specs(self, queryset): if include: queryset = queryset.filter(**include) if exclude: - queryset = queryset.exclude(**exclude) + queryset = queryset.exclude(reduce(operator.or_, + (Q(**{k: v}) for k, v in exclude.items()))) return queryset.distinct() From 42fe33c8a23ee6f5faaa66e7e4610503efbd121c Mon Sep 17 00:00:00 2001 From: Anatoly Ivanov Date: Thu, 25 Jan 2018 16:52:19 +0400 Subject: [PATCH 2/3] Fix filterset behavior on multiple negative conditions: simplify --- url_filter/backends/django.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/url_filter/backends/django.py b/url_filter/backends/django.py index dfa3560..c12c7aa 100644 --- a/url_filter/backends/django.py +++ b/url_filter/backends/django.py @@ -1,11 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals -import operator -from functools import reduce - from django.db.models.constants import LOOKUP_SEP -from django.db.models import Q from .base import BaseFilterBackend @@ -100,8 +96,7 @@ def filter_by_specs(self, queryset): if include: queryset = queryset.filter(**include) - if exclude: - queryset = queryset.exclude(reduce(operator.or_, - (Q(**{k: v}) for k, v in exclude.items()))) + for lookup, value in exclude.items(): + queryset = queryset.exclude(**{lookup: value}) return queryset.distinct() From 2947c9de71546f6dfd90ceb07c89a53a7c57a119 Mon Sep 17 00:00:00 2001 From: Anatoly Ivanov Date: Thu, 25 Jan 2018 17:19:30 +0400 Subject: [PATCH 3/3] Fix filterset behavior on multiple negative conditions: added comment --- url_filter/backends/django.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/url_filter/backends/django.py b/url_filter/backends/django.py index c12c7aa..8c1df11 100644 --- a/url_filter/backends/django.py +++ b/url_filter/backends/django.py @@ -96,6 +96,11 @@ def filter_by_specs(self, queryset): if include: queryset = queryset.filter(**include) + + # Plain queryset.exclude(**exclude) would cause exclusion of ALL + # negative-matching objects. I.e. x!=1&y!=2 is equivalent + # to "NOT (x = 1 AND y = 2)" SQL, which is not an intuitive behavior. + # We chain .exclude to achieve "NOT (x = 1) AND NOT (y = 2)" instead. for lookup, value in exclude.items(): queryset = queryset.exclude(**{lookup: value})