From 50fb7a52462fecf0127b38e7f3df322aeb287c43 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sun, 10 Feb 2013 21:40:09 +0100 Subject: [PATCH] Updated and added tests for QuerySet.datetimes. --- django/db/backends/__init__.py | 3 + tests/modeltests/timezones/tests.py | 128 +++++++++++++++---- tests/regressiontests/backends/tests.py | 4 +- tests/regressiontests/dates/models.py | 2 + tests/regressiontests/dates/tests.py | 38 +++--- tests/regressiontests/datetimes/__init__.py | 0 tests/regressiontests/datetimes/models.py | 28 ++++ tests/regressiontests/datetimes/tests.py | 83 ++++++++++++ tests/regressiontests/generic_views/dates.py | 5 +- 9 files changed, 241 insertions(+), 50 deletions(-) create mode 100644 tests/regressiontests/datetimes/__init__.py create mode 100644 tests/regressiontests/datetimes/models.py create mode 100644 tests/regressiontests/datetimes/tests.py diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py index bbb5a5b2941..38173615a38 100644 --- a/django/db/backends/__init__.py +++ b/django/db/backends/__init__.py @@ -397,6 +397,9 @@ class BaseDatabaseFeatures(object): # Can datetimes with timezones be used? supports_timezones = True + # Does the database have a copy of the zoneinfo database? + has_zoneinfo_database = True + # When performing a GROUP BY, is an ORDER BY NULL required # to remove any ordering? requires_explicit_null_ordering_when_grouping = False diff --git a/tests/modeltests/timezones/tests.py b/tests/modeltests/timezones/tests.py index 4ae6bbd6a82..8786c1912ff 100644 --- a/tests/modeltests/timezones/tests.py +++ b/tests/modeltests/timezones/tests.py @@ -189,13 +189,16 @@ class LegacyDatabaseTests(TestCase): self.assertEqual(Event.objects.filter(dt__gte=dt2).count(), 1) self.assertEqual(Event.objects.filter(dt__gt=dt2).count(), 0) - def test_query_date_related_filters(self): + def test_query_datetime_lookups(self): Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0)) Event.objects.create(dt=datetime.datetime(2011, 1, 1, 4, 30, 0)) self.assertEqual(Event.objects.filter(dt__year=2011).count(), 2) self.assertEqual(Event.objects.filter(dt__month=1).count(), 2) self.assertEqual(Event.objects.filter(dt__day=1).count(), 2) self.assertEqual(Event.objects.filter(dt__week_day=7).count(), 2) + self.assertEqual(Event.objects.filter(dt__hour=1).count(), 1) + self.assertEqual(Event.objects.filter(dt__minute=30).count(), 2) + self.assertEqual(Event.objects.filter(dt__second=0).count(), 2) def test_query_aggregation(self): # Only min and max make sense for datetimes. @@ -230,15 +233,30 @@ class LegacyDatabaseTests(TestCase): [afternoon_min_dt], transform=lambda d: d.dt) - def test_query_dates(self): + def test_query_datetimes(self): Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0)) Event.objects.create(dt=datetime.datetime(2011, 1, 1, 4, 30, 0)) - self.assertQuerysetEqual(Event.objects.dates('dt', 'year'), - [datetime.datetime(2011, 1, 1)], transform=lambda d: d) - self.assertQuerysetEqual(Event.objects.dates('dt', 'month'), - [datetime.datetime(2011, 1, 1)], transform=lambda d: d) - self.assertQuerysetEqual(Event.objects.dates('dt', 'day'), - [datetime.datetime(2011, 1, 1)], transform=lambda d: d) + self.assertQuerysetEqual(Event.objects.datetimes('dt', 'year'), + [datetime.datetime(2011, 1, 1, 0, 0, 0)], + transform=lambda d: d) + self.assertQuerysetEqual(Event.objects.datetimes('dt', 'month'), + [datetime.datetime(2011, 1, 1, 0, 0, 0)], + transform=lambda d: d) + self.assertQuerysetEqual(Event.objects.datetimes('dt', 'day'), + [datetime.datetime(2011, 1, 1, 0, 0, 0)], + transform=lambda d: d) + self.assertQuerysetEqual(Event.objects.datetimes('dt', 'hour'), + [datetime.datetime(2011, 1, 1, 1, 0, 0), + datetime.datetime(2011, 1, 1, 4, 0, 0)], + transform=lambda d: d) + self.assertQuerysetEqual(Event.objects.datetimes('dt', 'minute'), + [datetime.datetime(2011, 1, 1, 1, 30, 0), + datetime.datetime(2011, 1, 1, 4, 30, 0)], + transform=lambda d: d) + self.assertQuerysetEqual(Event.objects.datetimes('dt', 'second'), + [datetime.datetime(2011, 1, 1, 1, 30, 0), + datetime.datetime(2011, 1, 1, 4, 30, 0)], + transform=lambda d: d) def test_raw_sql(self): # Regression test for #17755 @@ -398,17 +416,32 @@ class NewDatabaseTests(TestCase): msg = str(warning.message) self.assertTrue(msg.startswith("DateTimeField received a naive datetime")) - def test_query_date_related_filters(self): - # These two dates fall in the same day in EAT, but in different days, - # years and months in UTC, and aggregation is performed in UTC when - # time zone support is enabled. This test could be changed if the - # implementation is changed to perform the aggregation is local time. + @skipUnlessDBFeature('has_zoneinfo_database') + def test_query_datetime_lookups(self): Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=EAT)) Event.objects.create(dt=datetime.datetime(2011, 1, 1, 4, 30, 0, tzinfo=EAT)) - self.assertEqual(Event.objects.filter(dt__year=2011).count(), 1) - self.assertEqual(Event.objects.filter(dt__month=1).count(), 1) - self.assertEqual(Event.objects.filter(dt__day=1).count(), 1) - self.assertEqual(Event.objects.filter(dt__week_day=7).count(), 1) + self.assertEqual(Event.objects.filter(dt__year=2011).count(), 2) + self.assertEqual(Event.objects.filter(dt__month=1).count(), 2) + self.assertEqual(Event.objects.filter(dt__day=1).count(), 2) + self.assertEqual(Event.objects.filter(dt__week_day=7).count(), 2) + self.assertEqual(Event.objects.filter(dt__hour=1).count(), 1) + self.assertEqual(Event.objects.filter(dt__minute=30).count(), 2) + self.assertEqual(Event.objects.filter(dt__second=0).count(), 2) + + @skipUnlessDBFeature('has_zoneinfo_database') + def test_query_datetime_lookups_in_other_timezone(self): + Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=EAT)) + Event.objects.create(dt=datetime.datetime(2011, 1, 1, 4, 30, 0, tzinfo=EAT)) + with timezone.override(UTC): + # These two dates fall in the same day in EAT, but in different days, + # years and months in UTC. + self.assertEqual(Event.objects.filter(dt__year=2011).count(), 1) + self.assertEqual(Event.objects.filter(dt__month=1).count(), 1) + self.assertEqual(Event.objects.filter(dt__day=1).count(), 1) + self.assertEqual(Event.objects.filter(dt__week_day=7).count(), 1) + self.assertEqual(Event.objects.filter(dt__hour=22).count(), 1) + self.assertEqual(Event.objects.filter(dt__minute=30).count(), 2) + self.assertEqual(Event.objects.filter(dt__second=0).count(), 2) def test_query_aggregation(self): # Only min and max make sense for datetimes. @@ -443,22 +476,61 @@ class NewDatabaseTests(TestCase): [afternoon_min_dt], transform=lambda d: d.dt) - def test_query_dates(self): - # Same comment as in test_query_date_related_filters. + @skipUnlessDBFeature('has_zoneinfo_database') + def test_query_datetimes(self): Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=EAT)) Event.objects.create(dt=datetime.datetime(2011, 1, 1, 4, 30, 0, tzinfo=EAT)) - self.assertQuerysetEqual(Event.objects.dates('dt', 'year'), - [datetime.datetime(2010, 1, 1, tzinfo=UTC), - datetime.datetime(2011, 1, 1, tzinfo=UTC)], + self.assertQuerysetEqual(Event.objects.datetimes('dt', 'year'), + [datetime.datetime(2011, 1, 1, 0, 0, 0, tzinfo=EAT)], transform=lambda d: d) - self.assertQuerysetEqual(Event.objects.dates('dt', 'month'), - [datetime.datetime(2010, 12, 1, tzinfo=UTC), - datetime.datetime(2011, 1, 1, tzinfo=UTC)], + self.assertQuerysetEqual(Event.objects.datetimes('dt', 'month'), + [datetime.datetime(2011, 1, 1, 0, 0, 0, tzinfo=EAT)], transform=lambda d: d) - self.assertQuerysetEqual(Event.objects.dates('dt', 'day'), - [datetime.datetime(2010, 12, 31, tzinfo=UTC), - datetime.datetime(2011, 1, 1, tzinfo=UTC)], + self.assertQuerysetEqual(Event.objects.datetimes('dt', 'day'), + [datetime.datetime(2011, 1, 1, 0, 0, 0, tzinfo=EAT)], transform=lambda d: d) + self.assertQuerysetEqual(Event.objects.datetimes('dt', 'hour'), + [datetime.datetime(2011, 1, 1, 1, 0, 0, tzinfo=EAT), + datetime.datetime(2011, 1, 1, 4, 0, 0, tzinfo=EAT)], + transform=lambda d: d) + self.assertQuerysetEqual(Event.objects.datetimes('dt', 'minute'), + [datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=EAT), + datetime.datetime(2011, 1, 1, 4, 30, 0, tzinfo=EAT)], + transform=lambda d: d) + self.assertQuerysetEqual(Event.objects.datetimes('dt', 'second'), + [datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=EAT), + datetime.datetime(2011, 1, 1, 4, 30, 0, tzinfo=EAT)], + transform=lambda d: d) + + @skipUnlessDBFeature('has_zoneinfo_database') + def test_query_datetimes_in_other_timezone(self): + Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=EAT)) + Event.objects.create(dt=datetime.datetime(2011, 1, 1, 4, 30, 0, tzinfo=EAT)) + with timezone.override(UTC): + self.assertQuerysetEqual(Event.objects.datetimes('dt', 'year'), + [datetime.datetime(2010, 1, 1, 0, 0, 0, tzinfo=UTC), + datetime.datetime(2011, 1, 1, 0, 0, 0, tzinfo=UTC)], + transform=lambda d: d) + self.assertQuerysetEqual(Event.objects.datetimes('dt', 'month'), + [datetime.datetime(2010, 12, 1, 0, 0, 0, tzinfo=UTC), + datetime.datetime(2011, 1, 1, 0, 0, 0, tzinfo=UTC)], + transform=lambda d: d) + self.assertQuerysetEqual(Event.objects.datetimes('dt', 'day'), + [datetime.datetime(2010, 12, 31, 0, 0, 0, tzinfo=UTC), + datetime.datetime(2011, 1, 1, 0, 0, 0, tzinfo=UTC)], + transform=lambda d: d) + self.assertQuerysetEqual(Event.objects.datetimes('dt', 'hour'), + [datetime.datetime(2010, 12, 31, 22, 0, 0, tzinfo=UTC), + datetime.datetime(2011, 1, 1, 1, 0, 0, tzinfo=UTC)], + transform=lambda d: d) + self.assertQuerysetEqual(Event.objects.datetimes('dt', 'minute'), + [datetime.datetime(2010, 12, 31, 22, 30, 0, tzinfo=UTC), + datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=UTC)], + transform=lambda d: d) + self.assertQuerysetEqual(Event.objects.datetimes('dt', 'second'), + [datetime.datetime(2010, 12, 31, 22, 30, 0, tzinfo=UTC), + datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=UTC)], + transform=lambda d: d) def test_raw_sql(self): # Regression test for #17755 diff --git a/tests/regressiontests/backends/tests.py b/tests/regressiontests/backends/tests.py index 313fdc83513..b915f4db8ac 100644 --- a/tests/regressiontests/backends/tests.py +++ b/tests/regressiontests/backends/tests.py @@ -146,9 +146,9 @@ class DateQuotingTest(TestCase): years = models.SchoolClass.objects.dates('last_updated', 'year') self.assertEqual(list(years), [datetime.datetime(2010, 1, 1, 0, 0)]) - def test_django_extract(self): + def test_django_date_extract(self): """ - Test the custom ``django_extract method``, in particular against fields + Test the custom ``django_date_extract method``, in particular against fields which clash with strings passed to it (e.g. 'day') - see #12818__. __: http://code.djangoproject.com/ticket/12818 diff --git a/tests/regressiontests/dates/models.py b/tests/regressiontests/dates/models.py index e4bffb71998..23350755e71 100644 --- a/tests/regressiontests/dates/models.py +++ b/tests/regressiontests/dates/models.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from django.db import models from django.utils.encoding import python_2_unicode_compatible diff --git a/tests/regressiontests/dates/tests.py b/tests/regressiontests/dates/tests.py index de28cac4366..6c02d597dec 100644 --- a/tests/regressiontests/dates/tests.py +++ b/tests/regressiontests/dates/tests.py @@ -1,6 +1,6 @@ from __future__ import absolute_import -from datetime import datetime +import datetime from django.test import TestCase @@ -11,32 +11,32 @@ class DatesTests(TestCase): def test_related_model_traverse(self): a1 = Article.objects.create( title="First one", - pub_date=datetime(2005, 7, 28), + pub_date=datetime.date(2005, 7, 28), ) a2 = Article.objects.create( title="Another one", - pub_date=datetime(2010, 7, 28), + pub_date=datetime.date(2010, 7, 28), ) a3 = Article.objects.create( title="Third one, in the first day", - pub_date=datetime(2005, 7, 28), + pub_date=datetime.date(2005, 7, 28), ) a1.comments.create( text="Im the HULK!", - pub_date=datetime(2005, 7, 28), + pub_date=datetime.date(2005, 7, 28), ) a1.comments.create( text="HULK SMASH!", - pub_date=datetime(2005, 7, 29), + pub_date=datetime.date(2005, 7, 29), ) a2.comments.create( text="LMAO", - pub_date=datetime(2010, 7, 28), + pub_date=datetime.date(2010, 7, 28), ) a3.comments.create( text="+1", - pub_date=datetime(2005, 8, 29), + pub_date=datetime.date(2005, 8, 29), ) c = Category.objects.create(name="serious-news") @@ -44,31 +44,31 @@ class DatesTests(TestCase): self.assertQuerysetEqual( Comment.objects.dates("article__pub_date", "year"), [ - datetime(2005, 1, 1), - datetime(2010, 1, 1), + datetime.date(2005, 1, 1), + datetime.date(2010, 1, 1), ], lambda d: d, ) self.assertQuerysetEqual( Comment.objects.dates("article__pub_date", "month"), [ - datetime(2005, 7, 1), - datetime(2010, 7, 1), + datetime.date(2005, 7, 1), + datetime.date(2010, 7, 1), ], lambda d: d ) self.assertQuerysetEqual( Comment.objects.dates("article__pub_date", "day"), [ - datetime(2005, 7, 28), - datetime(2010, 7, 28), + datetime.date(2005, 7, 28), + datetime.date(2010, 7, 28), ], lambda d: d ) self.assertQuerysetEqual( Article.objects.dates("comments__pub_date", "day"), [ - datetime(2005, 7, 28), - datetime(2005, 7, 29), - datetime(2005, 8, 29), - datetime(2010, 7, 28), + datetime.date(2005, 7, 28), + datetime.date(2005, 7, 29), + datetime.date(2005, 8, 29), + datetime.date(2010, 7, 28), ], lambda d: d ) @@ -77,7 +77,7 @@ class DatesTests(TestCase): ) self.assertQuerysetEqual( Category.objects.dates("articles__pub_date", "day"), [ - datetime(2005, 7, 28), + datetime.date(2005, 7, 28), ], lambda d: d, ) diff --git a/tests/regressiontests/datetimes/__init__.py b/tests/regressiontests/datetimes/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/regressiontests/datetimes/models.py b/tests/regressiontests/datetimes/models.py new file mode 100644 index 00000000000..f21376aa1c0 --- /dev/null +++ b/tests/regressiontests/datetimes/models.py @@ -0,0 +1,28 @@ +from __future__ import unicode_literals + +from django.db import models +from django.utils.encoding import python_2_unicode_compatible + + +@python_2_unicode_compatible +class Article(models.Model): + title = models.CharField(max_length=100) + pub_date = models.DateTimeField() + + categories = models.ManyToManyField("Category", related_name="articles") + + def __str__(self): + return self.title + +@python_2_unicode_compatible +class Comment(models.Model): + article = models.ForeignKey(Article, related_name="comments") + text = models.TextField() + pub_date = models.DateTimeField() + approval_date = models.DateTimeField(null=True) + + def __str__(self): + return 'Comment to %s (%s)' % (self.article.title, self.pub_date) + +class Category(models.Model): + name = models.CharField(max_length=255) diff --git a/tests/regressiontests/datetimes/tests.py b/tests/regressiontests/datetimes/tests.py new file mode 100644 index 00000000000..58cb060f6ba --- /dev/null +++ b/tests/regressiontests/datetimes/tests.py @@ -0,0 +1,83 @@ +from __future__ import absolute_import + +import datetime + +from django.test import TestCase + +from .models import Article, Comment, Category + + +class DateTimesTests(TestCase): + def test_related_model_traverse(self): + a1 = Article.objects.create( + title="First one", + pub_date=datetime.datetime(2005, 7, 28, 9, 0, 0), + ) + a2 = Article.objects.create( + title="Another one", + pub_date=datetime.datetime(2010, 7, 28, 10, 0, 0), + ) + a3 = Article.objects.create( + title="Third one, in the first day", + pub_date=datetime.datetime(2005, 7, 28, 17, 0, 0), + ) + + a1.comments.create( + text="Im the HULK!", + pub_date=datetime.datetime(2005, 7, 28, 9, 30, 0), + ) + a1.comments.create( + text="HULK SMASH!", + pub_date=datetime.datetime(2005, 7, 29, 1, 30, 0), + ) + a2.comments.create( + text="LMAO", + pub_date=datetime.datetime(2010, 7, 28, 10, 10, 10), + ) + a3.comments.create( + text="+1", + pub_date=datetime.datetime(2005, 8, 29, 10, 10, 10), + ) + + c = Category.objects.create(name="serious-news") + c.articles.add(a1, a3) + + self.assertQuerysetEqual( + Comment.objects.datetimes("article__pub_date", "year"), [ + datetime.datetime(2005, 1, 1), + datetime.datetime(2010, 1, 1), + ], + lambda d: d, + ) + self.assertQuerysetEqual( + Comment.objects.datetimes("article__pub_date", "month"), [ + datetime.datetime(2005, 7, 1), + datetime.datetime(2010, 7, 1), + ], + lambda d: d + ) + self.assertQuerysetEqual( + Comment.objects.datetimes("article__pub_date", "day"), [ + datetime.datetime(2005, 7, 28), + datetime.datetime(2010, 7, 28), + ], + lambda d: d + ) + self.assertQuerysetEqual( + Article.objects.datetimes("comments__pub_date", "day"), [ + datetime.datetime(2005, 7, 28), + datetime.datetime(2005, 7, 29), + datetime.datetime(2005, 8, 29), + datetime.datetime(2010, 7, 28), + ], + lambda d: d + ) + self.assertQuerysetEqual( + Article.objects.datetimes("comments__approval_date", "day"), [] + ) + self.assertQuerysetEqual( + Category.objects.datetimes("articles__pub_date", "day"), [ + datetime.datetime(2005, 7, 28), + ], + lambda d: d, + ) diff --git a/tests/regressiontests/generic_views/dates.py b/tests/regressiontests/generic_views/dates.py index 0c565daf9f3..dfb0eccb2d7 100644 --- a/tests/regressiontests/generic_views/dates.py +++ b/tests/regressiontests/generic_views/dates.py @@ -4,7 +4,7 @@ import time import datetime from django.core.exceptions import ImproperlyConfigured -from django.test import TestCase +from django.test import TestCase, skipUnlessDBFeature from django.test.utils import override_settings from django.utils import timezone from django.utils.unittest import skipUnless @@ -119,6 +119,7 @@ class ArchiveIndexViewTests(TestCase): self.assertEqual(res.status_code, 200) @requires_tz_support + @skipUnlessDBFeature('has_zoneinfo_database') @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi') def test_aware_datetime_archive_view(self): BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc)) @@ -204,6 +205,7 @@ class YearArchiveViewTests(TestCase): res = self.client.get('/dates/booksignings/2008/') self.assertEqual(res.status_code, 200) + @skipUnlessDBFeature('has_zoneinfo_database') @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi') def test_aware_datetime_year_view(self): BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc)) @@ -328,6 +330,7 @@ class MonthArchiveViewTests(TestCase): res = self.client.get('/dates/booksignings/2008/apr/') self.assertEqual(res.status_code, 200) + @skipUnlessDBFeature('has_zoneinfo_database') @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi') def test_aware_datetime_month_view(self): BookSigning.objects.create(event_date=datetime.datetime(2008, 2, 1, 12, 0, tzinfo=timezone.utc))