From b1c543d0910a79f4de93464fa684416c9441f7ce Mon Sep 17 00:00:00 2001 From: Adrian Holovaty Date: Sun, 17 Jul 2005 20:16:06 +0000 Subject: [PATCH] Factored out database-specific date_trunc behavior into dbmod.get_date_trunc_sql(). Refs #46 git-svn-id: http://code.djangoproject.com/svn/django/trunk@161 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/core/db/__init__.py | 1 + django/core/db/backends/mysql.py | 13 +++++++++++++ django/core/db/backends/postgresql.py | 6 ++++++ django/core/meta.py | 10 +++++++--- 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/django/core/db/__init__.py b/django/core/db/__init__.py index fc9dd75f61..17e3d2a7fd 100644 --- a/django/core/db/__init__.py +++ b/django/core/db/__init__.py @@ -29,5 +29,6 @@ dictfetchall = dbmod.dictfetchall dictfetchall = dbmod.dictfetchall get_last_insert_id = dbmod.get_last_insert_id get_date_extract_sql = dbmod.get_date_extract_sql +get_date_trunc_sql = dbmod.get_date_trunc_sql OPERATOR_MAPPING = dbmod.OPERATOR_MAPPING DATA_TYPES = dbmod.DATA_TYPES diff --git a/django/core/db/backends/mysql.py b/django/core/db/backends/mysql.py index 001a32c191..e3e3a5a299 100644 --- a/django/core/db/backends/mysql.py +++ b/django/core/db/backends/mysql.py @@ -64,8 +64,21 @@ def get_last_insert_id(cursor, table_name, pk_name): def get_date_extract_sql(lookup_type, table_name): # lookup_type is 'year', 'month', 'day' + # http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html return "EXTRACT(%s FROM %s)" % (lookup_type.upper(), table_name) +def get_date_trunc_sql(lookup_type, field_name): + # lookup_type is 'year', 'month', 'day' + # http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html + # MySQL doesn't support DATE_TRUNC, so we fake it by subtracting intervals. + # If you know of a better way to do this, please file a Django ticket. + subtractions = ["interval (DATE_FORMAT(%s, '%%%%s')) second - interval (DATE_FORMAT(%s, '%%%%i')) minute - interval (DATE_FORMAT(%s, '%%%%H')) hour" % (field_name, field_name, field_name)] + if lookup_type in ('year', 'month'): + subtractions.append(" - interval (DATE_FORMAT(%s, '%%%%e')-1) day" % field_name) + if lookup_type == 'year': + subtractions.append(" - interval (DATE_FORMAT(%s, '%%%%m')-1) month" % field_name) + return "(%s - %s)" % (field_name, ''.join(subtractions)) + OPERATOR_MAPPING = { 'exact': '=', 'iexact': 'LIKE', diff --git a/django/core/db/backends/postgresql.py b/django/core/db/backends/postgresql.py index df7829f683..8a629453b3 100644 --- a/django/core/db/backends/postgresql.py +++ b/django/core/db/backends/postgresql.py @@ -63,8 +63,14 @@ def get_last_insert_id(cursor, table_name, pk_name): def get_date_extract_sql(lookup_type, table_name): # lookup_type is 'year', 'month', 'day' + # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT return "EXTRACT('%s' FROM %s)" % (lookup_type, table_name) +def get_date_trunc_sql(lookup_type, field_name): + # lookup_type is 'year', 'month', 'day' + # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC + return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name) + # Register these custom typecasts, because Django expects dates/times to be # in Python's native (standard-library) datetime/time format, whereas psycopg # use mx.DateTime by default. diff --git a/django/core/meta.py b/django/core/meta.py index 8e7c22596e..a144cde8bd 100644 --- a/django/core/meta.py +++ b/django/core/meta.py @@ -1264,6 +1264,7 @@ def function_get_latest(opts, klass, does_not_exist_exception, **kwargs): return function_get_object(opts, klass, does_not_exist_exception, **kwargs) def function_get_date_list(opts, field, *args, **kwargs): + from django.core.db.typecasts import typecast_timestamp kind = args and args[0] or kwargs['kind'] assert kind in ("month", "year", "day"), "'kind' must be one of 'year', 'month' or 'day'." order = 'ASC' @@ -1275,10 +1276,13 @@ def function_get_date_list(opts, field, *args, **kwargs): if field.null: kwargs.setdefault('where', []).append('%s.%s IS NOT NULL' % (opts.db_table, field.name)) select, sql, params = function_get_sql_clause(opts, **kwargs) - sql = "SELECT DATE_TRUNC(%%s, %s.%s) %s GROUP BY 1 ORDER BY 1 %s" % (opts.db_table, field.name, sql, order) + sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1' % (db.get_date_trunc_sql(kind, '%s.%s' % (opts.db_table, field.name)), sql) cursor = db.db.cursor() - cursor.execute(sql, [kind] + params) - return [row[0] for row in cursor.fetchall()] + cursor.execute(sql, params) + # We have to manually run typecast_timestamp(str()) on the results, because + # MySQL doesn't automatically cast the result of date functions as datetime + # objects -- MySQL returns the values as strings, instead. + return [typecast_timestamp(str(row[0])) for row in cursor.fetchall()] ################################### # HELPER FUNCTIONS (MANIPULATORS) #