Loading django/db/backends/sqlite3/base.py +13 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ from django.db.backends.signals import connection_created from django.db.backends.sqlite3.client import DatabaseClient from django.db.backends.sqlite3.creation import DatabaseCreation from django.db.backends.sqlite3.introspection import DatabaseIntrospection from django.db.models import fields from django.db.models.sql import aggregates from django.utils.dateparse import parse_date, parse_datetime, parse_time from django.utils.functional import cached_property from django.utils.safestring import SafeBytes Loading Loading @@ -127,6 +129,17 @@ class DatabaseOperations(BaseDatabaseOperations): limit = 999 if len(fields) > 1 else 500 return (limit // len(fields)) if len(fields) > 0 else len(objs) def check_aggregate_support(self, aggregate): bad_fields = (fields.DateField, fields.DateTimeField, fields.TimeField) bad_aggregates = (aggregates.Sum, aggregates.Avg, aggregates.Variance, aggregates.StdDev) if (isinstance(aggregate.source, bad_fields) and isinstance(aggregate, bad_aggregates)): raise NotImplementedError( 'You cannot use Sum, Avg, StdDev and Variance aggregations ' 'on date/time fields in sqlite3 ' 'since date/time is saved as text.') def date_extract_sql(self, lookup_type, field_name): # sqlite doesn't support extract, so we fake it with the user-defined # function django_extract that's registered in connect(). Note that Loading docs/ref/models/querysets.txt +8 −0 Original line number Diff line number Diff line Loading @@ -2188,6 +2188,14 @@ Django provides the following aggregation functions in the aggregate functions, see :doc:`the topic guide on aggregation </topics/db/aggregation>`. .. warning:: SQLite can't handle aggregation on date/time fields out of the box. This is because there are no native date/time fields in SQLite and Django currently emulates these features using a text field. Attempts to use aggregation on date/time fields in SQLite will raise ``NotImplementedError``. Avg ~~~ Loading tests/regressiontests/backends/models.py +11 −0 Original line number Diff line number Diff line Loading @@ -75,3 +75,14 @@ class Article(models.Model): def __str__(self): return self.headline @python_2_unicode_compatible class Item(models.Model): name = models.CharField(max_length=30) date = models.DateField() time = models.TimeField() last_modified = models.DateTimeField() def __str__(self): return self.name tests/regressiontests/backends/tests.py +17 −1 Original line number Diff line number Diff line Loading @@ -12,6 +12,7 @@ from django.db import (backend, connection, connections, DEFAULT_DB_ALIAS, IntegrityError, transaction) from django.db.backends.signals import connection_created from django.db.backends.postgresql_psycopg2 import version as pg_version from django.db.models import fields, Sum, Avg, Variance, StdDev from django.db.utils import ConnectionHandler, DatabaseError, load_backend from django.test import (TestCase, skipUnlessDBFeature, skipIfDBFeature, TransactionTestCase) Loading Loading @@ -362,6 +363,22 @@ class EscapingChecks(TestCase): self.assertTrue(int(response)) class SqlliteAggregationTests(TestCase): """ #19360: Raise NotImplementedError when aggregating on date/time fields. """ @unittest.skipUnless(connection.vendor == 'sqlite', "No need to check SQLite aggregation semantics") def test_aggregation(self): for aggregate in (Sum, Avg, Variance, StdDev): self.assertRaises(NotImplementedError, models.Item.objects.all().aggregate, aggregate('time')) self.assertRaises(NotImplementedError, models.Item.objects.all().aggregate, aggregate('date')) self.assertRaises(NotImplementedError, models.Item.objects.all().aggregate, aggregate('last_modified')) class BackendTestCase(TestCase): def create_squares_with_executemany(self, args): Loading Loading @@ -400,7 +417,6 @@ class BackendTestCase(TestCase): self.create_squares_with_executemany(args) self.assertEqual(models.Square.objects.count(), 9) def test_unicode_fetches(self): #6254: fetchone, fetchmany, fetchall return strings as unicode objects qn = connection.ops.quote_name Loading Loading
django/db/backends/sqlite3/base.py +13 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ from django.db.backends.signals import connection_created from django.db.backends.sqlite3.client import DatabaseClient from django.db.backends.sqlite3.creation import DatabaseCreation from django.db.backends.sqlite3.introspection import DatabaseIntrospection from django.db.models import fields from django.db.models.sql import aggregates from django.utils.dateparse import parse_date, parse_datetime, parse_time from django.utils.functional import cached_property from django.utils.safestring import SafeBytes Loading Loading @@ -127,6 +129,17 @@ class DatabaseOperations(BaseDatabaseOperations): limit = 999 if len(fields) > 1 else 500 return (limit // len(fields)) if len(fields) > 0 else len(objs) def check_aggregate_support(self, aggregate): bad_fields = (fields.DateField, fields.DateTimeField, fields.TimeField) bad_aggregates = (aggregates.Sum, aggregates.Avg, aggregates.Variance, aggregates.StdDev) if (isinstance(aggregate.source, bad_fields) and isinstance(aggregate, bad_aggregates)): raise NotImplementedError( 'You cannot use Sum, Avg, StdDev and Variance aggregations ' 'on date/time fields in sqlite3 ' 'since date/time is saved as text.') def date_extract_sql(self, lookup_type, field_name): # sqlite doesn't support extract, so we fake it with the user-defined # function django_extract that's registered in connect(). Note that Loading
docs/ref/models/querysets.txt +8 −0 Original line number Diff line number Diff line Loading @@ -2188,6 +2188,14 @@ Django provides the following aggregation functions in the aggregate functions, see :doc:`the topic guide on aggregation </topics/db/aggregation>`. .. warning:: SQLite can't handle aggregation on date/time fields out of the box. This is because there are no native date/time fields in SQLite and Django currently emulates these features using a text field. Attempts to use aggregation on date/time fields in SQLite will raise ``NotImplementedError``. Avg ~~~ Loading
tests/regressiontests/backends/models.py +11 −0 Original line number Diff line number Diff line Loading @@ -75,3 +75,14 @@ class Article(models.Model): def __str__(self): return self.headline @python_2_unicode_compatible class Item(models.Model): name = models.CharField(max_length=30) date = models.DateField() time = models.TimeField() last_modified = models.DateTimeField() def __str__(self): return self.name
tests/regressiontests/backends/tests.py +17 −1 Original line number Diff line number Diff line Loading @@ -12,6 +12,7 @@ from django.db import (backend, connection, connections, DEFAULT_DB_ALIAS, IntegrityError, transaction) from django.db.backends.signals import connection_created from django.db.backends.postgresql_psycopg2 import version as pg_version from django.db.models import fields, Sum, Avg, Variance, StdDev from django.db.utils import ConnectionHandler, DatabaseError, load_backend from django.test import (TestCase, skipUnlessDBFeature, skipIfDBFeature, TransactionTestCase) Loading Loading @@ -362,6 +363,22 @@ class EscapingChecks(TestCase): self.assertTrue(int(response)) class SqlliteAggregationTests(TestCase): """ #19360: Raise NotImplementedError when aggregating on date/time fields. """ @unittest.skipUnless(connection.vendor == 'sqlite', "No need to check SQLite aggregation semantics") def test_aggregation(self): for aggregate in (Sum, Avg, Variance, StdDev): self.assertRaises(NotImplementedError, models.Item.objects.all().aggregate, aggregate('time')) self.assertRaises(NotImplementedError, models.Item.objects.all().aggregate, aggregate('date')) self.assertRaises(NotImplementedError, models.Item.objects.all().aggregate, aggregate('last_modified')) class BackendTestCase(TestCase): def create_squares_with_executemany(self, args): Loading Loading @@ -400,7 +417,6 @@ class BackendTestCase(TestCase): self.create_squares_with_executemany(args) self.assertEqual(models.Square.objects.count(), 9) def test_unicode_fetches(self): #6254: fetchone, fetchmany, fetchall return strings as unicode objects qn = connection.ops.quote_name Loading