diff --git a/django_filters/filters.py b/django_filters/filters.py index 0017291ce..f299f64a1 100644 --- a/django_filters/filters.py +++ b/django_filters/filters.py @@ -136,13 +136,30 @@ def field(self): self._field = self.field_class(label=self.label, **field_kwargs) return self._field + def get_filter_predicate(self, value): + lookup = '%s__%s' % (self.field_name, self.lookup_expr) + return {lookup: value} + + def _create_q_object(self, value): + q = Q(**self.get_filter_predicate(value)) + return ~q if self.exclude else q + + def get_q_objects(self, value): + if value in EMPTY_VALUES: + return (Q(), ) + return (self._create_q_object(value), ) + def filter(self, qs, value): if value in EMPTY_VALUES: return qs + if self.distinct: qs = qs.distinct() - lookup = '%s__%s' % (self.field_name, self.lookup_expr) - qs = self.get_method(qs)(**{lookup: value}) + + q_list = self.get_q_objects(value) + for q in q_list: + qs = qs.filter(q) + return qs @@ -161,12 +178,11 @@ def __init__(self, *args, **kwargs): self.null_value = kwargs.get('null_value', settings.NULL_CHOICE_VALUE) super().__init__(*args, **kwargs) - def filter(self, qs, value): + def get_q_objects(self, value): if value != self.null_value: - return super().filter(qs, value) + return super().get_q_objects(value) - qs = self.get_method(qs)(**{'%s__%s' % (self.field_name, self.lookup_expr): None}) - return qs.distinct() if self.distinct else qs + return (self._create_q_object(None), ) class TypedChoiceFilter(Filter): @@ -224,6 +240,21 @@ def is_noop(self, qs, value): return False + def get_q_objects(self, value): + q_list = [Q()] + for v in set(value): + if v == self.null_value: + v = None + if self.conjoined: + q_list.append(self._create_q_object(v)) + else: + q_list[0] |= Q(**self.get_filter_predicate(v)) + + if not self.conjoined and self.exclude: + q_list[0] = ~q_list[0] + + return q_list + def filter(self, qs, value): if not value: # Even though not a noop, no point filtering if empty. @@ -232,19 +263,9 @@ def filter(self, qs, value): if self.is_noop(qs, value): return qs - if not self.conjoined: - q = Q() - for v in set(value): - if v == self.null_value: - v = None - predicate = self.get_filter_predicate(v) - if self.conjoined: - qs = self.get_method(qs)(**predicate) - else: - q |= Q(**predicate) - - if not self.conjoined: - qs = self.get_method(qs)(q) + q_list = self.get_q_objects(value) + for q in q_list: + qs = qs.filter(q) return qs.distinct() if self.distinct else qs @@ -361,7 +382,7 @@ class NumberFilter(Filter): class NumericRangeFilter(Filter): field_class = RangeField - def filter(self, qs, value): + def get_q_objects(self, value): if value: if value.start is not None and value.stop is not None: value = (value.start, value.stop) @@ -372,13 +393,13 @@ def filter(self, qs, value): self.lookup_expr = 'endswith' value = value.stop - return super().filter(qs, value) + return super().get_q_objects(value) class RangeFilter(Filter): field_class = RangeField - def filter(self, qs, value): + def get_q_objects(self, value): if value: if value.start is not None and value.stop is not None: self.lookup_expr = 'range' @@ -390,7 +411,7 @@ def filter(self, qs, value): self.lookup_expr = 'lte' value = value.stop - return super().filter(qs, value) + return super().get_q_objects(value) def _truncate(dt): @@ -407,27 +428,27 @@ class DateRangeFilter(ChoiceFilter): ] filters = { - 'today': lambda qs, name: qs.filter(**{ - '%s__year' % name: now().year, - '%s__month' % name: now().month, - '%s__day' % name: now().day - }), - 'yesterday': lambda qs, name: qs.filter(**{ - '%s__year' % name: (now() - timedelta(days=1)).year, - '%s__month' % name: (now() - timedelta(days=1)).month, - '%s__day' % name: (now() - timedelta(days=1)).day, - }), - 'week': lambda qs, name: qs.filter(**{ - '%s__gte' % name: _truncate(now() - timedelta(days=7)), - '%s__lt' % name: _truncate(now() + timedelta(days=1)), - }), - 'month': lambda qs, name: qs.filter(**{ - '%s__year' % name: now().year, - '%s__month' % name: now().month - }), - 'year': lambda qs, name: qs.filter(**{ - '%s__year' % name: now().year, - }), + 'today': lambda name: ( + Q(**{'%s__year' % name: now().year}) & + Q(**{'%s__month' % name: now().month}) & + Q(**{'%s__day' % name: now().day}), + ), + 'yesterday': lambda name: ( + Q(**{'%s__year' % name: (now() - timedelta(days=1)).year}) & + Q(**{'%s__month' % name: (now() - timedelta(days=1)).month}) & + Q(**{'%s__day' % name: (now() - timedelta(days=1)).day}), + ), + 'week': lambda name: ( + Q(**{'%s__gte' % name: _truncate(now() - timedelta(days=7))}) & + Q(**{'%s__lt' % name: _truncate(now() + timedelta(days=1))}), + ), + 'month': lambda name: ( + Q(**{'%s__year' % name: now().year}) & + Q(**{'%s__month' % name: now().month}), + ), + 'year': lambda name: ( + Q(**{'%s__year' % name: now().year}), + ), } def __init__(self, choices=None, filters=None, *args, **kwargs): @@ -450,13 +471,16 @@ def __init__(self, choices=None, filters=None, *args, **kwargs): kwargs.setdefault('null_label', None) super().__init__(choices=self.choices, *args, **kwargs) + def get_q_objects(self, value): + assert value in self.filters + + return self.filters[value](self.field_name) + def filter(self, qs, value): if not value: return qs - assert value in self.filters - - qs = self.filters[value](qs, self.field_name) + qs = qs.filter(*self.get_q_objects(value)) return qs.distinct() if self.distinct else qs @@ -640,12 +664,12 @@ def field(self): return self._field - def filter(self, qs, lookup): + def get_q_objects(self, lookup): if not lookup: - return super(LookupChoiceFilter, self).filter(qs, None) + return super(LookupChoiceFilter, self).get_q_objects(None) self.lookup_expr = lookup.lookup_expr - return super(LookupChoiceFilter, self).filter(qs, lookup.value) + return super(LookupChoiceFilter, self).get_q_objects(lookup.value) class OrderingFilter(BaseCSVFilter, ChoiceFilter): @@ -702,6 +726,9 @@ def get_ordering_value(self, param): return "-%s" % field_name if descending else field_name + def get_q_objects(self, value): + return super().get_q_objects(None) + def filter(self, qs, value): if value in EMPTY_VALUES: return qs diff --git a/django_filters/filterset.py b/django_filters/filterset.py index d174718c0..5dca4cc94 100644 --- a/django_filters/filterset.py +++ b/django_filters/filterset.py @@ -25,6 +25,7 @@ ModelChoiceFilter, ModelMultipleChoiceFilter, NumberFilter, + OrderingFilter, TimeFilter, UUIDFilter ) @@ -226,13 +227,41 @@ def filter_queryset(self, queryset): This method should be overridden if additional filtering needs to be applied to the queryset before it is cached. """ - for name, value in self.form.cleaned_data.items(): - queryset = self.filters[name].filter(queryset, value) - assert isinstance(queryset, models.QuerySet), \ - "Expected '%s.%s' to return a QuerySet, but got a %s instead." \ - % (type(self).__name__, name, type(queryset).__name__) + filter_map = self.build_filter_map() + for name, filter_ in filter_map.items(): + queryset = queryset.filter(*filter_['q_list']) + if filter_['distinct']: + queryset = queryset.distinct() + return self.order_queryset(queryset) + + def order_queryset(self, queryset): + """ + Orders the filtered query set after it has been filtered. + """ + order_filters = ( + (self.filters[name], value) + for name, value in self.form.cleaned_data.items() + if isinstance(self.filters[name], OrderingFilter) + ) + for filter_, value in order_filters: + queryset = filter_.filter(queryset, value) return queryset + def build_filter_map(self): + """ + Builds a map of the generated `Q` object lists with additional meta data. + + This method should be overridden if more complex filters needs to be applied. + """ + filter_map = {} + for name, value in self.form.cleaned_data.items(): + q_list = self.filters[name].get_q_objects(value) + filter_map[name] = { + 'q_list': q_list, + 'distinct': self.filters[name].distinct + } + return filter_map + @property def qs(self): if not hasattr(self, '_qs'): diff --git a/tests/test_filtering.py b/tests/test_filtering.py index 18f2b4821..b6b7072cf 100644 --- a/tests/test_filtering.py +++ b/tests/test_filtering.py @@ -5,6 +5,7 @@ from operator import attrgetter from django import forms +from django.db import models from django.http import QueryDict from django.test import TestCase, override_settings from django.utils import timezone @@ -1926,7 +1927,7 @@ class Meta: qs = MockQuerySet() F({'account': 'jdoe'}, queryset=qs).qs - qs.all.return_value.filter.assert_called_with(username__exact='jdoe') + qs.all.return_value.filter.assert_called_with(models.Q(username__exact='jdoe')) def test_filtering_without_meta(self): class F(FilterSet): diff --git a/tests/test_filters.py b/tests/test_filters.py index d63074d7f..65342141f 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -4,6 +4,7 @@ from datetime import date, datetime, time, timedelta from django import forms +from django.db import models from django.test import TestCase, override_settings from django.utils import translation from django.utils.translation import gettext as _ @@ -126,21 +127,21 @@ def test_filtering(self): qs = mock.Mock(spec=['filter']) f = Filter() result = f.filter(qs, 'value') - qs.filter.assert_called_once_with(None__exact='value') + qs.filter.assert_called_once_with(models.Q(None__exact='value')) self.assertNotEqual(qs, result) def test_filtering_exclude(self): qs = mock.Mock(spec=['filter', 'exclude']) f = Filter(exclude=True) result = f.filter(qs, 'value') - qs.exclude.assert_called_once_with(None__exact='value') + qs.filter.assert_called_once_with(~models.Q(None__exact='value')) self.assertNotEqual(qs, result) def test_filtering_uses_name(self): qs = mock.Mock(spec=['filter']) f = Filter(field_name='somefield') f.filter(qs, 'value') - result = qs.filter.assert_called_once_with(somefield__exact='value') + result = qs.filter.assert_called_once_with(models.Q(somefield__exact='value')) self.assertNotEqual(qs, result) def test_filtering_skipped_with_blank_value(self): @@ -200,14 +201,14 @@ def test_filtering(self): qs = mock.Mock(spec=['filter']) f = BooleanFilter(field_name='somefield') result = f.filter(qs, True) - qs.filter.assert_called_once_with(somefield__exact=True) + qs.filter.assert_called_once_with(models.Q(somefield__exact=True)) self.assertNotEqual(qs, result) def test_filtering_exclude(self): - qs = mock.Mock(spec=['exclude']) + qs = mock.Mock(spec=['filter']) f = BooleanFilter(field_name='somefield', exclude=True) result = f.filter(qs, True) - qs.exclude.assert_called_once_with(somefield__exact=True) + qs.filter.assert_called_once_with(~models.Q(somefield__exact=True)) self.assertNotEqual(qs, result) def test_filtering_skipped_with_blank_value(self): @@ -228,7 +229,7 @@ def test_filtering_lookup_expr(self): qs = mock.Mock(spec=['filter']) f = BooleanFilter(field_name='somefield', lookup_expr='isnull') result = f.filter(qs, True) - qs.filter.assert_called_once_with(somefield__isnull=True) + qs.filter.assert_called_once_with(models.Q(somefield__isnull=True)) self.assertNotEqual(qs, result) @@ -396,7 +397,7 @@ def test_filtering(self): qs.filter.return_value.distinct.assert_called_once_with() def test_filtering_exclude(self): - qs = mock.Mock(spec=['exclude']) + qs = mock.Mock(spec=['filter']) f = MultipleChoiceFilter(field_name='somefield', exclude=True) with mock.patch('django_filters.filters.Q') as mockQclass: mockQ1, mockQ2 = mock.MagicMock(), mock.MagicMock() @@ -407,8 +408,8 @@ def test_filtering_exclude(self): self.assertEqual(mockQclass.call_args_list, [mock.call(), mock.call(somefield='value')]) mockQ1.__ior__.assert_called_once_with(mockQ2) - qs.exclude.assert_called_once_with(mockQ1.__ior__.return_value) - qs.exclude.return_value.distinct.assert_called_once_with() + qs.filter.assert_called_once_with(mockQ1.__ior__.return_value.__invert__.return_value) + qs.filter.return_value.distinct.assert_called_once_with() def test_filtering_with_lookup_expr(self): qs = mock.Mock(spec=['filter']) @@ -548,7 +549,7 @@ def test_filtering(self): qs.filter.return_value.distinct.assert_called_once_with() def test_filtering_exclude(self): - qs = mock.Mock(spec=['exclude']) + qs = mock.Mock(spec=['filter']) f = TypedMultipleChoiceFilter(field_name='somefield', exclude=True) with mock.patch('django_filters.filters.Q') as mockQclass: mockQ1, mockQ2 = mock.MagicMock(), mock.MagicMock() @@ -559,8 +560,8 @@ def test_filtering_exclude(self): self.assertEqual(mockQclass.call_args_list, [mock.call(), mock.call(somefield='value')]) mockQ1.__ior__.assert_called_once_with(mockQ2) - qs.exclude.assert_called_once_with(mockQ1.__ior__.return_value) - qs.exclude.return_value.distinct.assert_called_once_with() + qs.filter.assert_called_once_with(mockQ1.__ior__.return_value.__invert__.return_value) + qs.filter.return_value.distinct.assert_called_once_with() def test_filtering_on_required_skipped_when_len_of_value_is_len_of_field_choices(self): qs = mock.Mock(spec=[]) @@ -805,21 +806,21 @@ def test_filtering(self): qs = mock.Mock(spec=['filter']) f = NumberFilter() f.filter(qs, 1) - qs.filter.assert_called_once_with(None__exact=1) + qs.filter.assert_called_once_with(models.Q(None__exact=1)) # Also test 0 as it once had a bug qs.reset_mock() f.filter(qs, 0) - qs.filter.assert_called_once_with(None__exact=0) + qs.filter.assert_called_once_with(models.Q(None__exact=0)) def test_filtering_exclude(self): - qs = mock.Mock(spec=['exclude']) + qs = mock.Mock(spec=['filter']) f = NumberFilter(exclude=True) f.filter(qs, 1) - qs.exclude.assert_called_once_with(None__exact=1) + qs.filter.assert_called_once_with(~models.Q(None__exact=1)) # Also test 0 as it once had a bug qs.reset_mock() f.filter(qs, 0) - qs.exclude.assert_called_once_with(None__exact=0) + qs.filter.assert_called_once_with(~models.Q(None__exact=0)) class NumericRangeFilterTests(TestCase): @@ -834,14 +835,14 @@ def test_filtering(self): value = mock.Mock(start=20, stop=30) f = NumericRangeFilter() f.filter(qs, value) - qs.filter.assert_called_once_with(None__exact=(20, 30)) + qs.filter.assert_called_once_with(models.Q(None__exact=(20, 30))) def test_filtering_exclude(self): - qs = mock.Mock(spec=['exclude']) + qs = mock.Mock(spec=['filter']) value = mock.Mock(start=20, stop=30) f = NumericRangeFilter(exclude=True) f.filter(qs, value) - qs.exclude.assert_called_once_with(None__exact=(20, 30)) + qs.filter.assert_called_once_with(~models.Q(None__exact=(20, 30))) def test_filtering_skipped_with_none_value(self): qs = mock.Mock(spec=['filter']) @@ -854,28 +855,28 @@ def test_field_with_lookup_expr(self): value = mock.Mock(start=20, stop=30) f = NumericRangeFilter(lookup_expr=('overlap')) f.filter(qs, value) - qs.filter.assert_called_once_with(None__overlap=(20, 30)) + qs.filter.assert_called_once_with(models.Q(None__overlap=(20, 30))) def test_zero_to_zero(self): qs = mock.Mock(spec=['filter']) value = mock.Mock(start=0, stop=0) f = NumericRangeFilter() f.filter(qs, value) - qs.filter.assert_called_once_with(None__exact=(0, 0)) + qs.filter.assert_called_once_with(models.Q(None__exact=(0, 0))) def test_filtering_startswith(self): qs = mock.Mock(spec=['filter']) value = mock.Mock(start=20, stop=None) f = NumericRangeFilter() f.filter(qs, value) - qs.filter.assert_called_once_with(None__startswith=20) + qs.filter.assert_called_once_with(models.Q(None__startswith=20)) def test_filtering_endswith(self): qs = mock.Mock(spec=['filter']) value = mock.Mock(start=None, stop=30) f = NumericRangeFilter() f.filter(qs, value) - qs.filter.assert_called_once_with(None__endswith=30) + qs.filter.assert_called_once_with(models.Q(None__endswith=30)) def test_filtering_distinct(self): f = NumericRangeFilter(distinct=True) @@ -884,19 +885,19 @@ def test_filtering_distinct(self): qs = mock.Mock() f.filter(qs, mock.Mock(start=20, stop=30)) qs.distinct.assert_called_once() - qs.distinct.return_value.filter.assert_called_once_with(None__exact=(20, 30)) + qs.distinct.return_value.filter.assert_called_once_with(models.Q(None__exact=(20, 30))) # min qs = mock.Mock() f.filter(qs, mock.Mock(start=20, stop=None)) qs.distinct.assert_called_once() - qs.distinct.return_value.filter.assert_called_once_with(None__startswith=20) + qs.distinct.return_value.filter.assert_called_once_with(models.Q(None__startswith=20)) # max qs = mock.Mock() f.filter(qs, mock.Mock(start=None, stop=30)) qs.distinct.assert_called_once() - qs.distinct.return_value.filter.assert_called_once_with(None__endswith=30) + qs.distinct.return_value.filter.assert_called_once_with(models.Q(None__endswith=30)) class RangeFilterTests(TestCase): @@ -911,28 +912,28 @@ def test_filtering_range(self): value = mock.Mock(start=20, stop=30) f = RangeFilter() f.filter(qs, value) - qs.filter.assert_called_once_with(None__range=(20, 30)) + qs.filter.assert_called_once_with(models.Q(None__range=(20, 30))) def test_filtering_exclude(self): - qs = mock.Mock(spec=['exclude']) + qs = mock.Mock(spec=['filter']) value = mock.Mock(start=20, stop=30) f = RangeFilter(exclude=True) f.filter(qs, value) - qs.exclude.assert_called_once_with(None__range=(20, 30)) + qs.filter.assert_called_once_with(~models.Q(None__range=(20, 30))) def test_filtering_start(self): qs = mock.Mock(spec=['filter']) value = mock.Mock(start=20, stop=None) f = RangeFilter() f.filter(qs, value) - qs.filter.assert_called_once_with(None__gte=20) + qs.filter.assert_called_once_with(models.Q(None__gte=20)) def test_filtering_stop(self): qs = mock.Mock(spec=['filter']) value = mock.Mock(start=None, stop=30) f = RangeFilter() f.filter(qs, value) - qs.filter.assert_called_once_with(None__lte=30) + qs.filter.assert_called_once_with(models.Q(None__lte=30)) def test_filtering_skipped_with_none_value(self): qs = mock.Mock(spec=['filter']) @@ -945,7 +946,7 @@ def test_filtering_ignores_lookup_expr(self): value = mock.Mock(start=20, stop=30) f = RangeFilter(lookup_expr='gte') f.filter(qs, value) - qs.filter.assert_called_once_with(None__range=(20, 30)) + qs.filter.assert_called_once_with(models.Q(None__range=(20, 30))) def test_filtering_distinct(self): f = RangeFilter(distinct=True) @@ -954,19 +955,19 @@ def test_filtering_distinct(self): qs = mock.Mock() f.filter(qs, mock.Mock(start=20, stop=30)) qs.distinct.assert_called_once() - qs.distinct.return_value.filter.assert_called_once_with(None__range=(20, 30)) + qs.distinct.return_value.filter.assert_called_once_with(models.Q(None__range=(20, 30))) # min qs = mock.Mock() f.filter(qs, mock.Mock(start=20, stop=None)) qs.distinct.assert_called_once() - qs.distinct.return_value.filter.assert_called_once_with(None__gte=20) + qs.distinct.return_value.filter.assert_called_once_with(models.Q(None__gte=20)) # max qs = mock.Mock() f.filter(qs, mock.Mock(start=None, stop=30)) qs.distinct.assert_called_once() - qs.distinct.return_value.filter.assert_called_once_with(None__lte=30) + qs.distinct.return_value.filter.assert_called_once_with(models.Q(None__lte=30)) class DateRangeFilterTests(TestCase): @@ -996,7 +997,7 @@ def test_filtering(self): def test_filtering_skipped_with_out_of_range_value(self): # Field validation should prevent this from occuring - qs = mock.Mock(spec=[]) + qs = mock.Mock(spec=['filter']) f = DateRangeFilter() with self.assertRaises(AssertionError): f.filter(qs, 'tomorrow') @@ -1023,7 +1024,7 @@ def test_filtering_for_this_year(self): f = DateRangeFilter() f.filter(qs, 'year') qs.filter.assert_called_once_with( - None__year=now_dt.year) + models.Q(None__year=now_dt.year)) def test_filtering_for_this_month(self): qs = mock.Mock(spec=['filter']) @@ -1032,7 +1033,7 @@ def test_filtering_for_this_month(self): f = DateRangeFilter() f.filter(qs, 'month') qs.filter.assert_called_once_with( - None__year=now_dt.year, None__month=now_dt.month) + models.Q(None__year=now_dt.year) & models.Q(None__month=now_dt.month)) def test_filtering_for_7_days(self): qs = mock.Mock(spec=['filter']) @@ -1047,7 +1048,8 @@ def test_filtering_for_7_days(self): mock_td.call_args_list, [mock.call(days=7), mock.call(days=1)] ) - qs.filter.assert_called_once_with(None__lt=mock_d2, None__gte=mock_d1) + qs.filter.assert_called_once_with( + models.Q(None__gte=mock_d1) & models.Q(None__lt=mock_d2)) def test_filtering_for_today(self): qs = mock.Mock(spec=['filter']) @@ -1056,9 +1058,9 @@ def test_filtering_for_today(self): f = DateRangeFilter() f.filter(qs, 'today') qs.filter.assert_called_once_with( - None__year=now_dt.year, - None__month=now_dt.month, - None__day=now_dt.day) + models.Q(None__year=now_dt.year) & + models.Q(None__month=now_dt.month) & + models.Q(None__day=now_dt.day)) def test_filtering_for_yesterday(self): qs = mock.Mock(spec=['filter']) @@ -1067,9 +1069,9 @@ def test_filtering_for_yesterday(self): f = DateRangeFilter() f.filter(qs, 'yesterday') qs.filter.assert_called_once_with( - None__year=(now_dt - timedelta(days=1)).year, - None__month=(now_dt - timedelta(days=1)).month, - None__day=(now_dt - timedelta(days=1)).day, + models.Q(None__year=(now_dt - timedelta(days=1)).year) & + models.Q(None__month=(now_dt - timedelta(days=1)).month) & + models.Q(None__day=(now_dt - timedelta(days=1)).day), ) @@ -1086,21 +1088,21 @@ def test_filtering_range(self): f = DateFromToRangeFilter() f.filter(qs, value) qs.filter.assert_called_once_with( - None__range=(date(2015, 4, 7), date(2015, 9, 6))) + models.Q(None__range=(date(2015, 4, 7), date(2015, 9, 6)))) def test_filtering_start(self): qs = mock.Mock(spec=['filter']) value = mock.Mock(start=date(2015, 4, 7), stop=None) f = DateFromToRangeFilter() f.filter(qs, value) - qs.filter.assert_called_once_with(None__gte=date(2015, 4, 7)) + qs.filter.assert_called_once_with(models.Q(None__gte=date(2015, 4, 7))) def test_filtering_stop(self): qs = mock.Mock(spec=['filter']) value = mock.Mock(start=None, stop=date(2015, 9, 6)) f = DateFromToRangeFilter() f.filter(qs, value) - qs.filter.assert_called_once_with(None__lte=date(2015, 9, 6)) + qs.filter.assert_called_once_with(models.Q(None__lte=date(2015, 9, 6))) def test_filtering_skipped_with_none_value(self): qs = mock.Mock(spec=['filter']) @@ -1114,7 +1116,7 @@ def test_filtering_ignores_lookup_expr(self): f = DateFromToRangeFilter(lookup_expr='gte') f.filter(qs, value) qs.filter.assert_called_once_with( - None__range=(date(2015, 4, 7), date(2015, 9, 6))) + models.Q(None__range=(date(2015, 4, 7), date(2015, 9, 6)))) class DateTimeFromToRangeFilterTests(TestCase): @@ -1131,21 +1133,21 @@ def test_filtering_range(self): f = DateTimeFromToRangeFilter() f.filter(qs, value) qs.filter.assert_called_once_with( - None__range=(datetime(2015, 4, 7, 8, 30), datetime(2015, 9, 6, 11, 45))) + models.Q(None__range=(datetime(2015, 4, 7, 8, 30), datetime(2015, 9, 6, 11, 45)))) def test_filtering_start(self): qs = mock.Mock(spec=['filter']) value = mock.Mock(start=datetime(2015, 4, 7, 8, 30), stop=None) f = DateTimeFromToRangeFilter() f.filter(qs, value) - qs.filter.assert_called_once_with(None__gte=datetime(2015, 4, 7, 8, 30)) + qs.filter.assert_called_once_with(models.Q(None__gte=datetime(2015, 4, 7, 8, 30))) def test_filtering_stop(self): qs = mock.Mock(spec=['filter']) value = mock.Mock(start=None, stop=datetime(2015, 9, 6, 11, 45)) f = DateTimeFromToRangeFilter() f.filter(qs, value) - qs.filter.assert_called_once_with(None__lte=datetime(2015, 9, 6, 11, 45)) + qs.filter.assert_called_once_with(models.Q(None__lte=datetime(2015, 9, 6, 11, 45))) def test_filtering_skipped_with_none_value(self): qs = mock.Mock(spec=['filter']) @@ -1160,7 +1162,7 @@ def test_filtering_ignores_lookup_expr(self): f = DateTimeFromToRangeFilter(lookup_expr='gte') f.filter(qs, value) qs.filter.assert_called_once_with( - None__range=(datetime(2015, 4, 7, 8, 30), datetime(2015, 9, 6, 11, 45))) + models.Q(None__range=(datetime(2015, 4, 7, 8, 30), datetime(2015, 9, 6, 11, 45)))) class IsoDateTimeFromToRangeFilterTests(TestCase): @@ -1177,21 +1179,21 @@ def test_filtering_range(self): f = IsoDateTimeFromToRangeFilter() f.filter(qs, value) qs.filter.assert_called_once_with( - None__range=(datetime(2015, 4, 7, 8, 30), datetime(2015, 9, 6, 11, 45))) + models.Q(None__range=(datetime(2015, 4, 7, 8, 30), datetime(2015, 9, 6, 11, 45)))) def test_filtering_start(self): qs = mock.Mock(spec=['filter']) value = mock.Mock(start=datetime(2015, 4, 7, 8, 30), stop=None) f = IsoDateTimeFromToRangeFilter() f.filter(qs, value) - qs.filter.assert_called_once_with(None__gte=datetime(2015, 4, 7, 8, 30)) + qs.filter.assert_called_once_with(models.Q(None__gte=datetime(2015, 4, 7, 8, 30))) def test_filtering_stop(self): qs = mock.Mock(spec=['filter']) value = mock.Mock(start=None, stop=datetime(2015, 9, 6, 11, 45)) f = IsoDateTimeFromToRangeFilter() f.filter(qs, value) - qs.filter.assert_called_once_with(None__lte=datetime(2015, 9, 6, 11, 45)) + qs.filter.assert_called_once_with(models.Q(None__lte=datetime(2015, 9, 6, 11, 45))) def test_filtering_skipped_with_none_value(self): qs = mock.Mock(spec=['filter']) @@ -1206,7 +1208,7 @@ def test_filtering_ignores_lookup_expr(self): f = IsoDateTimeFromToRangeFilter(lookup_expr='gte') f.filter(qs, value) qs.filter.assert_called_once_with( - None__range=(datetime(2015, 4, 7, 8, 30), datetime(2015, 9, 6, 11, 45))) + models.Q(None__range=(datetime(2015, 4, 7, 8, 30), datetime(2015, 9, 6, 11, 45)))) class TimeRangeFilterTests(TestCase): @@ -1222,21 +1224,21 @@ def test_filtering_range(self): f = TimeRangeFilter() f.filter(qs, value) qs.filter.assert_called_once_with( - None__range=(time(10, 15), time(12, 30))) + models.Q(None__range=(time(10, 15), time(12, 30)))) def test_filtering_start(self): qs = mock.Mock(spec=['filter']) value = mock.Mock(start=time(10, 15), stop=None) f = TimeRangeFilter() f.filter(qs, value) - qs.filter.assert_called_once_with(None__gte=time(10, 15)) + qs.filter.assert_called_once_with(models.Q(None__gte=time(10, 15))) def test_filtering_stop(self): qs = mock.Mock(spec=['filter']) value = mock.Mock(start=None, stop=time(12, 30)) f = TimeRangeFilter() f.filter(qs, value) - qs.filter.assert_called_once_with(None__lte=time(12, 30)) + qs.filter.assert_called_once_with(models.Q(None__lte=time(12, 30))) def test_filtering_skipped_with_none_value(self): qs = mock.Mock(spec=['filter']) @@ -1250,7 +1252,7 @@ def test_filtering_ignores_lookup_expr(self): f = TimeRangeFilter(lookup_expr='gte') f.filter(qs, value) qs.filter.assert_called_once_with( - None__range=(time(10, 15), time(12, 30))) + models.Q(None__range=(time(10, 15), time(12, 30)))) class AllValuesFilterTests(TestCase): @@ -1351,7 +1353,7 @@ def test_filtering(self): qs = mock.Mock(spec=['filter']) f = LookupChoiceFilter(field_name='somefield', lookup_choices=['some_lookup_expr']) result = f.filter(qs, Lookup('value', 'some_lookup_expr')) - qs.filter.assert_called_once_with(somefield__some_lookup_expr='value') + qs.filter.assert_called_once_with(models.Q(somefield__some_lookup_expr='value')) self.assertNotEqual(qs, result) @@ -1386,7 +1388,7 @@ def test_filtering(self): qs = mock.Mock(spec=['filter']) f = self.number_in f.filter(qs, [1, 2]) - qs.filter.assert_called_once_with(None__in=[1, 2]) + qs.filter.assert_called_once_with(models.Q(None__in=[1, 2])) def test_filtering_skipped_with_none_value(self): qs = mock.Mock(spec=['filter']) @@ -1398,7 +1400,7 @@ def test_field_with_lookup_expr(self): qs = mock.Mock() f = self.datetimeyear_in f.filter(qs, [1, 2]) - qs.filter.assert_called_once_with(None__year__in=[1, 2]) + qs.filter.assert_called_once_with(models.Q(None__year__in=[1, 2])) class BaseInFilterTests(TestCase): @@ -1409,7 +1411,7 @@ class NumberInFilter(BaseInFilter, NumberFilter): qs = mock.Mock(spec=['filter']) f = NumberInFilter() f.filter(qs, [1, 2]) - qs.filter.assert_called_once_with(None__in=[1, 2]) + qs.filter.assert_called_once_with(models.Q(None__in=[1, 2])) class BaseRangeFilterTests(TestCase): @@ -1420,7 +1422,7 @@ class NumberInFilter(BaseRangeFilter, NumberFilter): qs = mock.Mock(spec=['filter']) f = NumberInFilter() f.filter(qs, [1, 2]) - qs.filter.assert_called_once_with(None__range=[1, 2]) + qs.filter.assert_called_once_with(models.Q(None__range=[1, 2])) class OrderingFilterTests(TestCase):