diff --git a/django/core/db/base.py b/django/core/db/base.py deleted file mode 100644 index 62cca9d0be..0000000000 --- a/django/core/db/base.py +++ /dev/null @@ -1,32 +0,0 @@ -from time import time - -class CursorDebugWrapper: - def __init__(self, cursor, db): - self.cursor = cursor - self.db = db - - def execute(self, sql, params=[]): - start = time() - result = self.cursor.execute(sql, params) - stop = time() - self.db.queries.append({ - 'sql': sql % tuple(params), - 'time': "%.3f" % (stop - start), - }) - return result - - def executemany(self, sql, param_list): - start = time() - result = self.cursor.executemany(sql, param_list) - stop = time() - self.db.queries.append({ - 'sql': 'MANY: ' + sql + ' ' + str(tuple(param_list)), - 'time': "%.3f" % (stop - start), - }) - return result - - def __getattr__(self, attr): - if self.__dict__.has_key(attr): - return self.__dict__[attr] - else: - return getattr(self.cursor, attr) diff --git a/django/core/db/dicthelpers.py b/django/core/db/dicthelpers.py deleted file mode 100644 index 5aedc51aed..0000000000 --- a/django/core/db/dicthelpers.py +++ /dev/null @@ -1,24 +0,0 @@ -""" -Helper functions for dictfetch* for databases that don't natively support them. -""" - -def _dict_helper(desc, row): - "Returns a dictionary for the given cursor.description and result row." - return dict([(desc[col[0]][0], col[1]) for col in enumerate(row)]) - -def dictfetchone(cursor): - "Returns a row from the cursor as a dict" - row = cursor.fetchone() - if not row: - return None - return _dict_helper(cursor.description, row) - -def dictfetchmany(cursor, number): - "Returns a certain number of rows from a cursor as a dict" - desc = cursor.description - return [_dict_helper(desc, row) for row in cursor.fetchmany(number)] - -def dictfetchall(cursor): - "Returns all rows from a cursor as a dict" - desc = cursor.description - return [_dict_helper(desc, row) for row in cursor.fetchall()] diff --git a/django/core/db/typecasts.py b/django/core/db/typecasts.py deleted file mode 100644 index e78eab0180..0000000000 --- a/django/core/db/typecasts.py +++ /dev/null @@ -1,54 +0,0 @@ -import datetime - -############################################### -# Converters from database (string) to Python # -############################################### - -def typecast_date(s): - return s and datetime.date(*map(int, s.split('-'))) or None # returns None if s is null - -def typecast_time(s): # does NOT store time zone information - if not s: return None - hour, minutes, seconds = s.split(':') - if '.' in seconds: # check whether seconds have a fractional part - seconds, microseconds = seconds.split('.') - else: - microseconds = '0' - return datetime.time(int(hour), int(minutes), int(seconds), int(float('.'+microseconds) * 1000000)) - -def typecast_timestamp(s): # does NOT store time zone information - # "2005-07-29 15:48:00.590358-05" - # "2005-07-29 09:56:00-05" - if not s: return None - d, t = s.split() - # Extract timezone information, if it exists. Currently we just throw - # it away, but in the future we may make use of it. - if '-' in t: - t, tz = t.split('-', 1) - tz = '-' + tz - elif '+' in t: - t, tz = t.split('+', 1) - tz = '+' + tz - else: - tz = '' - dates = d.split('-') - times = t.split(':') - seconds = times[2] - if '.' in seconds: # check whether seconds have a fractional part - seconds, microseconds = seconds.split('.') - else: - microseconds = '0' - return datetime.datetime(int(dates[0]), int(dates[1]), int(dates[2]), - int(times[0]), int(times[1]), int(seconds), int(float('.'+microseconds) * 1000000)) - -def typecast_boolean(s): - if s is None: return None - if not s: return False - return str(s)[0].lower() == 't' - -############################################### -# Converters from Python to database (string) # -############################################### - -def rev_typecast_boolean(obj, d): - return obj and '1' or '0' diff --git a/django/db/__init__.py b/django/db/__init__.py index fde0745659..3cf3b6bbc9 100644 --- a/django/db/__init__.py +++ b/django/db/__init__.py @@ -10,7 +10,7 @@ except ImportError, e: from django.core.exceptions import ImproperlyConfigured import os backend_dir = os.path.join(__path__[0], 'backends') - available_backends = [f[:-3] for f in os.listdir(backend_dir) if not f.startswith('_')] + available_backends = [f for f in os.listdir(backend_dir) if not f.startswith('_') and not f.startswith('.')] available_backends.sort() raise ImproperlyConfigured, "Could not load database backend: %s. Is your DATABASE_ENGINE setting (currently, %r) spelled correctly? Available options are: %s" % \ (e, DATABASE_ENGINE, ", ".join(map(repr, available_backends))) diff --git a/django/db/backends/ado_mssql/base.py b/django/db/backends/ado_mssql/base.py index 44fb4ec1c9..2c854ee811 100644 --- a/django/db/backends/ado_mssql/base.py +++ b/django/db/backends/ado_mssql/base.py @@ -4,8 +4,7 @@ ADO MSSQL database backend for Django. Requires adodbapi 2.0.1: http://adodbapi.sourceforge.net/ """ -from django.core.db import base -from django.core.db.dicthelpers import * +from django.db.backends import util import adodbapi as Database import datetime try: @@ -82,6 +81,10 @@ class DatabaseWrapper: return name # Quoting once is enough. return '[%s]' % name +dictfetchone = util.dictfetchone +dictfetchmany = util.dictfetchmany +dictfetchall = util.dictfetchall + def get_last_insert_id(cursor, table_name, pk_name): cursor.execute("SELECT %s FROM %s WHERE %s = @@IDENTITY" % (pk_name, table_name, pk_name)) return cursor.fetchone()[0] @@ -151,4 +154,3 @@ DATA_TYPES = { 'URLField': 'varchar(200)', 'USStateField': 'varchar(2)', } - diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index 0f1f29bd82..3d4e0e9ff6 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -4,8 +4,7 @@ MySQL database backend for Django. Requires MySQLdb: http://sourceforge.net/projects/mysql-python """ -from django.core.db import base, typecasts -from django.core.db.dicthelpers import * +from django.db.backends import util import MySQLdb as Database from MySQLdb.converters import conversions from MySQLdb.constants import FIELD_TYPE @@ -15,10 +14,10 @@ DatabaseError = Database.DatabaseError django_conversions = conversions.copy() django_conversions.update({ - types.BooleanType: typecasts.rev_typecast_boolean, - FIELD_TYPE.DATETIME: typecasts.typecast_timestamp, - FIELD_TYPE.DATE: typecasts.typecast_date, - FIELD_TYPE.TIME: typecasts.typecast_time, + types.BooleanType: util.rev_typecast_boolean, + FIELD_TYPE.DATETIME: util.typecast_timestamp, + FIELD_TYPE.DATE: util.typecast_date, + FIELD_TYPE.TIME: util.typecast_time, }) # This is an extra debug layer over MySQL queries, to display warnings. @@ -66,7 +65,7 @@ class DatabaseWrapper: kwargs['port'] = DATABASE_PORT self.connection = Database.connect(**kwargs) if DEBUG: - return base.CursorDebugWrapper(MysqlDebugWrapper(self.connection.cursor()), self) + return util.CursorDebugWrapper(MysqlDebugWrapper(self.connection.cursor()), self) return self.connection.cursor() def commit(self): @@ -89,6 +88,10 @@ class DatabaseWrapper: return name # Quoting once is enough. return "`%s`" % name +dictfetchone = util.dictfetchone +dictfetchmany = util.dictfetchmany +dictfetchall = util.dictfetchall + def get_last_insert_id(cursor, table_name, pk_name): cursor.execute("SELECT LAST_INSERT_ID()") return cursor.fetchone()[0] diff --git a/django/db/backends/postgresql/base.py b/django/db/backends/postgresql/base.py index 730d7d4649..8d57e7e21e 100644 --- a/django/db/backends/postgresql/base.py +++ b/django/db/backends/postgresql/base.py @@ -4,7 +4,7 @@ PostgreSQL database backend for Django. Requires psycopg 1: http://initd.org/projects/psycopg1 """ -from django.core.db import base, typecasts +from django.db.backends import util import psycopg as Database DatabaseError = Database.DatabaseError @@ -34,7 +34,7 @@ class DatabaseWrapper: cursor = self.connection.cursor() cursor.execute("SET TIME ZONE %s", [TIME_ZONE]) if DEBUG: - return base.CursorDebugWrapper(cursor, self) + return util.CursorDebugWrapper(cursor, self) return cursor def commit(self): @@ -93,12 +93,12 @@ def get_random_function_sql(): # in Python's native (standard-library) datetime/time format, whereas psycopg # use mx.DateTime by default. try: - Database.register_type(Database.new_type((1082,), "DATE", typecasts.typecast_date)) + Database.register_type(Database.new_type((1082,), "DATE", util.typecast_date)) except AttributeError: raise Exception, "You appear to be using psycopg version 2, which isn't supported yet, because it's still in beta. Use psycopg version 1 instead: http://initd.org/projects/psycopg1" -Database.register_type(Database.new_type((1083,1266), "TIME", typecasts.typecast_time)) -Database.register_type(Database.new_type((1114,1184), "TIMESTAMP", typecasts.typecast_timestamp)) -Database.register_type(Database.new_type((16,), "BOOLEAN", typecasts.typecast_boolean)) +Database.register_type(Database.new_type((1083,1266), "TIME", util.typecast_time)) +Database.register_type(Database.new_type((1114,1184), "TIMESTAMP", util.typecast_timestamp)) +Database.register_type(Database.new_type((16,), "BOOLEAN", util.typecast_boolean)) OPERATOR_MAPPING = { 'exact': '= %s', diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py index 8525ccce54..047a1ddfc7 100644 --- a/django/db/backends/sqlite3/base.py +++ b/django/db/backends/sqlite3/base.py @@ -2,17 +2,16 @@ SQLite3 backend for django. Requires pysqlite2 (http://pysqlite.org/). """ -from django.core.db import base, typecasts -from django.core.db.dicthelpers import * +from django.db.backends import util from pysqlite2 import dbapi2 as Database DatabaseError = Database.DatabaseError # Register adaptors ########################################################### Database.register_converter("bool", lambda s: str(s) == '1') -Database.register_converter("time", typecasts.typecast_time) -Database.register_converter("date", typecasts.typecast_date) -Database.register_converter("datetime", typecasts.typecast_timestamp) +Database.register_converter("time", util.typecast_time) +Database.register_converter("date", util.typecast_date) +Database.register_converter("datetime", util.typecast_timestamp) # Database wrapper ############################################################ @@ -39,7 +38,7 @@ class DatabaseWrapper: cursor = self.connection.cursor(factory=SQLiteCursorWrapper) cursor.row_factory = utf8rowFactory if DEBUG: - return base.CursorDebugWrapper(cursor, self) + return util.CursorDebugWrapper(cursor, self) else: return cursor @@ -60,6 +59,10 @@ class DatabaseWrapper: return name # Quoting once is enough. return '"%s"' % name +dictfetchone = util.dictfetchone +dictfetchmany = util.dictfetchmany +dictfetchall = util.dictfetchall + class SQLiteCursorWrapper(Database.Cursor): """ Django uses "format" style placeholders, but pysqlite2 uses "qmark" style. @@ -92,7 +95,7 @@ def get_date_extract_sql(lookup_type, table_name): def _sqlite_extract(lookup_type, dt): try: - dt = typecasts.typecast_timestamp(dt) + dt = util.typecast_timestamp(dt) except (ValueError, TypeError): return None return str(getattr(dt, lookup_type)) @@ -113,7 +116,7 @@ def get_random_function_sql(): def _sqlite_date_trunc(lookup_type, dt): try: - dt = typecasts.typecast_timestamp(dt) + dt = util.typecast_timestamp(dt) except (ValueError, TypeError): return None if lookup_type == 'year': diff --git a/django/db/backends/util.py b/django/db/backends/util.py new file mode 100644 index 0000000000..a9d78dcfde --- /dev/null +++ b/django/db/backends/util.py @@ -0,0 +1,111 @@ +import datetime +from time import time + +class CursorDebugWrapper: + def __init__(self, cursor, db): + self.cursor = cursor + self.db = db + + def execute(self, sql, params=[]): + start = time() + result = self.cursor.execute(sql, params) + stop = time() + self.db.queries.append({ + 'sql': sql % tuple(params), + 'time': "%.3f" % (stop - start), + }) + return result + + def executemany(self, sql, param_list): + start = time() + result = self.cursor.executemany(sql, param_list) + stop = time() + self.db.queries.append({ + 'sql': 'MANY: ' + sql + ' ' + str(tuple(param_list)), + 'time': "%.3f" % (stop - start), + }) + return result + + def __getattr__(self, attr): + if self.__dict__.has_key(attr): + return self.__dict__[attr] + else: + return getattr(self.cursor, attr) + +############################################### +# Converters from database (string) to Python # +############################################### + +def typecast_date(s): + return s and datetime.date(*map(int, s.split('-'))) or None # returns None if s is null + +def typecast_time(s): # does NOT store time zone information + if not s: return None + hour, minutes, seconds = s.split(':') + if '.' in seconds: # check whether seconds have a fractional part + seconds, microseconds = seconds.split('.') + else: + microseconds = '0' + return datetime.time(int(hour), int(minutes), int(seconds), int(float('.'+microseconds) * 1000000)) + +def typecast_timestamp(s): # does NOT store time zone information + # "2005-07-29 15:48:00.590358-05" + # "2005-07-29 09:56:00-05" + if not s: return None + d, t = s.split() + # Extract timezone information, if it exists. Currently we just throw + # it away, but in the future we may make use of it. + if '-' in t: + t, tz = t.split('-', 1) + tz = '-' + tz + elif '+' in t: + t, tz = t.split('+', 1) + tz = '+' + tz + else: + tz = '' + dates = d.split('-') + times = t.split(':') + seconds = times[2] + if '.' in seconds: # check whether seconds have a fractional part + seconds, microseconds = seconds.split('.') + else: + microseconds = '0' + return datetime.datetime(int(dates[0]), int(dates[1]), int(dates[2]), + int(times[0]), int(times[1]), int(seconds), int(float('.'+microseconds) * 1000000)) + +def typecast_boolean(s): + if s is None: return None + if not s: return False + return str(s)[0].lower() == 't' + +############################################### +# Converters from Python to database (string) # +############################################### + +def rev_typecast_boolean(obj, d): + return obj and '1' or '0' + +################################################################################## +# Helper functions for dictfetch* for databases that don't natively support them # +################################################################################## + +def _dict_helper(desc, row): + "Returns a dictionary for the given cursor.description and result row." + return dict([(desc[col[0]][0], col[1]) for col in enumerate(row)]) + +def dictfetchone(cursor): + "Returns a row from the cursor as a dict" + row = cursor.fetchone() + if not row: + return None + return _dict_helper(cursor.description, row) + +def dictfetchmany(cursor, number): + "Returns a certain number of rows from a cursor as a dict" + desc = cursor.description + return [_dict_helper(desc, row) for row in cursor.fetchmany(number)] + +def dictfetchall(cursor): + "Returns all rows from a cursor as a dict" + desc = cursor.description + return [_dict_helper(desc, row) for row in cursor.fetchall()]