From 79d44435e0e05b4dd70e719b217e39d1f1899ac4 Mon Sep 17 00:00:00 2001 From: Shawn Campbell Date: Fri, 5 Sep 2014 16:30:56 -0400 Subject: [PATCH 1/3] #1338 added tests validating that filtering against isoformat works correctly unless you filter a date against a datetime. --- tests/models.py | 7 ++++ tests/test_filters.py | 84 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/tests/models.py b/tests/models.py index fe064b46a..4f59f7552 100644 --- a/tests/models.py +++ b/tests/models.py @@ -71,6 +71,13 @@ class FilterableItem(BaseFilterableItem): decimal = models.DecimalField(max_digits=4, decimal_places=2) date = models.DateField() +#Model of test for #1338 + + +class FilterableISO8601Item(BaseFilterableItem): + decimal = models.DecimalField(max_digits=4, decimal_places=2) + date = models.DateTimeField() + # Model for regression test for #285 diff --git a/tests/test_filters.py b/tests/test_filters.py index 47bffd436..7bbeb81b1 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -9,7 +9,7 @@ from django.conf.urls import patterns, url from rest_framework import generics, serializers, status, filters from rest_framework.compat import django_filters from rest_framework.test import APIRequestFactory -from .models import BaseFilterableItem, FilterableItem, BasicModel +from .models import BaseFilterableItem, FilterableItem, FilterableISO8601Item, BasicModel from .utils import temporary_setting factory = APIRequestFactory() @@ -683,3 +683,85 @@ class SensitiveOrderingFilterTests(TestCase): {'id': 3, username_field: 'userC'}, # PassA ] ) + + +class Iso8601DateFilter(TestCase): + """ + Test cases around the filtering of Iso8601 formated dates + """ + + def _serialize_object(self, obj): + return {'id': obj.id, 'text': obj.text, 'decimal': obj.decimal, 'date': obj.date} + + def setUp(self): + """ + Set up 2 filterable ISO8601 format dates + """ + base_data = ('a', Decimal('0.25'), datetime.date(2012, 10, 8)) + for i in range(2): + text = chr(i + ord(base_data[0])) * 3 # Produces string 'aaa', 'bbb', etc.decimal = base_data[1] + i + date = base_data[2] - datetime.timedelta(days=i * 2) + decimal = base_data[1] + i + FilterableItem(text=text, decimal=decimal, date=date.isoformat()).save() + + self.objects = FilterableItem.objects + self.data = [ + self._serialize_object(obj) + for obj in self.objects.all() + ] + + def test_filter_isoformat_date(self): + view = FilterFieldsRootView.as_view() + # Tests that the date filter works with ISO8601 format. + search_date = datetime.date(2012, 9, 22) + request = factory.get('/', {'date': '%s' % search_date.isoformat()}) # search_date str: '2012-09-22' + response = view(request).render() + self.assertEqual(response.status_code, status.HTTP_200_OK) + expected_data = [f for f in self.data if f['date'] == search_date] + self.assertEqual(response.data, expected_data) + + def test_filter_isoformat_datetime_against_date_fails(self): + view = FilterFieldsRootView.as_view() + # Tests that the date filter fails with an ISO8601 datetime format. + search_date = datetime.datetime(2012, 9, 22, 12, 00, 00) + request = factory.get('/', {'date': '%s' % search_date.isoformat()}) # search_date str: '2012-09-22T12:00:00' + response = view(request).render() + self.assertEqual(response.status_code, status.HTTP_200_OK) + expected_data = [f for f in self.data if f['date'] == search_date] + self.assertEqual(response.data, expected_data) + + +class Iso8601DateTimeFilter(TestCase): + """ + Test cases around the filtering of Iso8601 formated datetimes + """ + + def _serialize_object(self, obj): + return {'id': obj.id, 'text': obj.text, 'decimal': obj.decimal, 'date': obj.date} + + def setUp(self): + """ + Set up 2 filterable ISO8601 format datetimes + """ + base_data = ('a', Decimal('0.25'), datetime.datetime(2012, 10, 8, 12, 00, 00)) + for i in range(2): + text = chr(i + ord(base_data[0])) * 3 # Produces string 'aaa', 'bbb', etc. + decimal = base_data[1] + i + date = base_data[2] - datetime.timedelta(days=i * 2) + FilterableISO8601Item(text=text, decimal=decimal, date=date.isoformat()).save() + + self.objects = FilterableISO8601Item.objects + self.data = [ + self._serialize_object(obj) + for obj in self.objects.all() + ] + + def test_filter_isoformat_datetime(self): + view = FilterFieldsRootView.as_view() + # Tests that the date filter works with ISO8601 format. + search_date = datetime.datetime(2012, 9, 22, 12, 00, 00) + request = factory.get('/', {'date': '%s' % search_date.isoformat()}) # search_date str: '2012-09-22' + response = view(request).render() + self.assertEqual(response.status_code, status.HTTP_200_OK) + expected_data = [f for f in self.data if f['date'] == search_date] + self.assertEqual(response.data, expected_data) From bd225c49ce821931980c8ae0332598c922a77080 Mon Sep 17 00:00:00 2001 From: Shawn Campbell Date: Fri, 5 Sep 2014 17:50:22 -0400 Subject: [PATCH 2/3] #1338 found the issue, its the filter construction. Dates should not be iexacted but a simple equals query. --- .gitignore | 3 +++ tests/test_filters.py | 19 ++++++++++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index ae73f8379..088160a36 100644 --- a/.gitignore +++ b/.gitignore @@ -16,5 +16,8 @@ include/ lib/ local/ +*.iml +atlassian-ide-plugin.xml + !.gitignore !.travis.yml diff --git a/tests/test_filters.py b/tests/test_filters.py index 7bbeb81b1..a1b3b2a00 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -723,11 +723,13 @@ class Iso8601DateFilter(TestCase): def test_filter_isoformat_datetime_against_date_fails(self): view = FilterFieldsRootView.as_view() # Tests that the date filter fails with an ISO8601 datetime format. - search_date = datetime.datetime(2012, 9, 22, 12, 00, 00) + search_date = datetime.datetime(2012, 8, 12, 12, 00, 00) request = factory.get('/', {'date': '%s' % search_date.isoformat()}) # search_date str: '2012-09-22T12:00:00' response = view(request).render() self.assertEqual(response.status_code, status.HTTP_200_OK) expected_data = [f for f in self.data if f['date'] == search_date] + # This should be [] because date != datetime + self.assertEqual(response.data, []) self.assertEqual(response.data, expected_data) @@ -737,7 +739,7 @@ class Iso8601DateTimeFilter(TestCase): """ def _serialize_object(self, obj): - return {'id': obj.id, 'text': obj.text, 'decimal': obj.decimal, 'date': obj.date} + return {'id': obj.id, 'text': obj.text, 'decimal': obj.decimal, 'date': obj.date.isoformat()} def setUp(self): """ @@ -759,9 +761,16 @@ class Iso8601DateTimeFilter(TestCase): def test_filter_isoformat_datetime(self): view = FilterFieldsRootView.as_view() # Tests that the date filter works with ISO8601 format. - search_date = datetime.datetime(2012, 9, 22, 12, 00, 00) + search_date = datetime.datetime(2012, 10, 8, 12, 00, 00) request = factory.get('/', {'date': '%s' % search_date.isoformat()}) # search_date str: '2012-09-22' response = view(request).render() self.assertEqual(response.status_code, status.HTTP_200_OK) - expected_data = [f for f in self.data if f['date'] == search_date] - self.assertEqual(response.data, expected_data) + expected_data = [f for f in self.data if f['date'] == search_date.isoformat()] + # This shows that the you cannot filter via an isoformatted datetime + self.assertNotEqual(response.data, expected_data) + # This shows that the issues is not Django filters itself but how the filters are built via iexact + should_find_something = FilterableISO8601Item.objects.filter(date__iexact=search_date.isoformat()) + should_find_something_2 = FilterableISO8601Item.objects.filter(date=search_date.isoformat()) + self.assertEqual(should_find_something.count(), 0) + # The issue is that dates are being filtered via an iexact filter which does date comparision + self.assertNotEqual(should_find_something.query.__str__(), should_find_something_2.query.__str__()) From b54c76b9fd8a6bcb85b4b6e27194786106190762 Mon Sep 17 00:00:00 2001 From: Shawn Campbell Date: Fri, 5 Sep 2014 17:56:50 -0400 Subject: [PATCH 3/3] cleaned up test a little to show problem in a cleaner way --- #.gitignore# | 23 +++++++++++++++++++++++ tests/test_filters.py | 8 ++++---- 2 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 #.gitignore# diff --git a/#.gitignore# b/#.gitignore# new file mode 100644 index 000000000..088160a36 --- /dev/null +++ b/#.gitignore# @@ -0,0 +1,23 @@ +*.pyc +*.db +*~ +.* + +html/ +htmlcov/ +coverage/ +build/ +dist/ +*.egg-info/ +MANIFEST + +bin/ +include/ +lib/ +local/ + +*.iml +atlassian-ide-plugin.xml + +!.gitignore +!.travis.yml diff --git a/tests/test_filters.py b/tests/test_filters.py index a1b3b2a00..bc3e070a9 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -769,8 +769,8 @@ class Iso8601DateTimeFilter(TestCase): # This shows that the you cannot filter via an isoformatted datetime self.assertNotEqual(response.data, expected_data) # This shows that the issues is not Django filters itself but how the filters are built via iexact - should_find_something = FilterableISO8601Item.objects.filter(date__iexact=search_date.isoformat()) - should_find_something_2 = FilterableISO8601Item.objects.filter(date=search_date.isoformat()) - self.assertEqual(should_find_something.count(), 0) + should_not_find_something = FilterableISO8601Item.objects.filter(date__iexact=search_date.isoformat()) + should_find_something = FilterableISO8601Item.objects.filter(date=search_date.isoformat()) + self.assertEqual(should_not_find_something.count(), 0) # The issue is that dates are being filtered via an iexact filter which does date comparision - self.assertNotEqual(should_find_something.query.__str__(), should_find_something_2.query.__str__()) + self.assertNotEqual(should_find_something.count(), should_not_find_something.count())