diff --git a/django/db/__init__.py b/django/db/__init__.py index 72adf60d66bda..e1af430dc24e3 100644 --- a/django/db/__init__.py +++ b/django/db/__init__.py @@ -1,20 +1,25 @@ import warnings from django.core import signals -from django.db.utils import (DEFAULT_DB_ALIAS, - DataError, OperationalError, IntegrityError, InternalError, - ProgrammingError, NotSupportedError, DatabaseError, - InterfaceError, Error, - load_backend, ConnectionHandler, ConnectionRouter) +from django.db.utils import (DEFAULT_DB_ALIAS, DataError, OperationalError, + IntegrityError, InternalError, ProgrammingError, NotSupportedError, + DatabaseError, InterfaceError, Error, load_backend, + ConnectionHandler, ConnectionRouter) from django.utils.functional import cached_property -__all__ = ('backend', 'connection', 'connections', 'router', 'DatabaseError', - 'IntegrityError', 'DEFAULT_DB_ALIAS') + +__all__ = [ + 'backend', 'connection', 'connections', 'router', 'DatabaseError', + 'IntegrityError', 'InternalError', 'ProgrammingError', 'DataError', + 'NotSupportedError', 'Error', 'InterfaceError', 'OperationalError', + 'DEFAULT_DB_ALIAS' +] connections = ConnectionHandler() router = ConnectionRouter() + # `connection`, `DatabaseError` and `IntegrityError` are convenient aliases # for backend bits. @@ -70,6 +75,7 @@ def __delattr__(self, name): backend = DefaultBackendProxy() + def close_connection(**kwargs): warnings.warn( "close_connection is superseded by close_old_connections.", @@ -83,12 +89,14 @@ def close_connection(**kwargs): transaction.abort(conn) connections[conn].close() + # Register an event to reset saved queries when a Django request is started. def reset_queries(**kwargs): for conn in connections.all(): conn.queries = [] signals.request_started.connect(reset_queries) + # Register an event to reset transaction state and close connections past # their lifetime. NB: abort() doesn't do anything outside of a transaction. def close_old_connections(**kwargs): diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py index 28a4a48a9f8fc..a553351c5c7ba 100644 --- a/django/db/backends/__init__.py +++ b/django/db/backends/__init__.py @@ -1167,6 +1167,7 @@ def modify_insert_params(self, placeholders, params): 'name type_code display_size internal_size precision scale null_ok' ) + class BaseDatabaseIntrospection(object): """ This class encapsulates all backend-specific introspection utilities diff --git a/django/db/backends/creation.py b/django/db/backends/creation.py index 6f66cfb7ca905..50e45c0e75050 100644 --- a/django/db/backends/creation.py +++ b/django/db/backends/creation.py @@ -251,12 +251,13 @@ def sql_remove_table_constraints(self, model, references_to_delete, style): r_col = model._meta.get_field(f.rel.field_name).column r_name = '%s_refs_%s_%s' % ( col, r_col, self._digest(table, r_table)) - output.append('%s %s %s %s;' % \ - (style.SQL_KEYWORD('ALTER TABLE'), + output.append('%s %s %s %s;' % ( + style.SQL_KEYWORD('ALTER TABLE'), style.SQL_TABLE(qn(table)), style.SQL_KEYWORD(self.connection.ops.drop_foreignkey_sql()), style.SQL_FIELD(qn(truncate_name( - r_name, self.connection.ops.max_name_length()))))) + r_name, self.connection.ops.max_name_length()))) + )) del references_to_delete[model] return output diff --git a/django/db/backends/dummy/base.py b/django/db/backends/dummy/base.py index 9a220ffd8b5ca..3e6d3e4c5a2ba 100644 --- a/django/db/backends/dummy/base.py +++ b/django/db/backends/dummy/base.py @@ -8,33 +8,43 @@ """ from django.core.exceptions import ImproperlyConfigured -from django.db.backends import * +from django.db.backends import (BaseDatabaseOperations, BaseDatabaseClient, + BaseDatabaseIntrospection, BaseDatabaseWrapper, BaseDatabaseFeatures, + BaseDatabaseValidation) from django.db.backends.creation import BaseDatabaseCreation + def complain(*args, **kwargs): raise ImproperlyConfigured("settings.DATABASES is improperly configured. " "Please supply the ENGINE value. Check " "settings documentation for more details.") + def ignore(*args, **kwargs): pass + class DatabaseError(Exception): pass + class IntegrityError(DatabaseError): pass + class DatabaseOperations(BaseDatabaseOperations): quote_name = complain + class DatabaseClient(BaseDatabaseClient): runshell = complain + class DatabaseCreation(BaseDatabaseCreation): create_test_db = ignore destroy_test_db = ignore + class DatabaseIntrospection(BaseDatabaseIntrospection): get_table_list = complain get_table_description = complain @@ -42,6 +52,7 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): get_indexes = complain get_key_columns = complain + class DatabaseWrapper(BaseDatabaseWrapper): operators = {} # Override the base class implementations with null diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index d10be94f43969..2db746f40fb2e 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -36,8 +36,9 @@ pytz = None from django.conf import settings -from django.db import utils -from django.db.backends import * +from django.db import (utils, BaseDatabaseFeatures, BaseDatabaseOperations, + BaseDatabaseWrapper) +from django.db.backends import util from django.db.backends.mysql.client import DatabaseClient from django.db.backends.mysql.creation import DatabaseCreation from django.db.backends.mysql.introspection import DatabaseIntrospection @@ -57,6 +58,7 @@ # It's impossible to import datetime_or_None directly from MySQLdb.times parse_datetime = conversions[FIELD_TYPE.DATETIME] + def parse_datetime_with_timezone_support(value): dt = parse_datetime(value) # Confirm that dt is naive before overwriting its tzinfo. @@ -64,6 +66,7 @@ def parse_datetime_with_timezone_support(value): dt = dt.replace(tzinfo=timezone.utc) return dt + def adapt_datetime_with_timezone_support(value, conv): # Equivalent to DateTimeField.get_db_prep_value. Used only by raw SQL. if settings.USE_TZ: @@ -98,6 +101,7 @@ def adapt_datetime_with_timezone_support(value, conv): # http://dev.mysql.com/doc/refman/5.0/en/news.html . server_version_re = re.compile(r'(\d{1,2})\.(\d{1,2})\.(\d{1,2})') + # MySQLdb-1.2.1 and newer automatically makes use of SHOW WARNINGS on # MySQL-4.1 and newer, so the MysqlDebugWrapper is unnecessary. Since the # point is to raise Warnings as exceptions, this can be done with the Python @@ -148,6 +152,7 @@ def __getattr__(self, attr): def __iter__(self): return iter(self.cursor) + class DatabaseFeatures(BaseDatabaseFeatures): empty_fetchmany_value = () update_can_self_select = False @@ -204,6 +209,7 @@ def has_zoneinfo_database(self): cursor.execute("SELECT 1 FROM mysql.time_zone LIMIT 1") return cursor.fetchone() is not None + class DatabaseOperations(BaseDatabaseOperations): compiler_module = "django.db.backends.mysql.compiler" @@ -319,7 +325,7 @@ def sequence_reset_by_name_sql(self, style, sequences): # Truncate already resets the AUTO_INCREMENT field from # MySQL version 5.0.13 onwards. Refs #16961. if self.connection.mysql_version < (5, 0, 13): - return ["%s %s %s %s %s;" % \ + return ["%s %s %s %s %s;" % (style.SQL_KEYWORD('ALTER'), style.SQL_KEYWORD('TABLE'), style.SQL_TABLE(self.quote_name(sequence['table'])), @@ -373,6 +379,7 @@ def bulk_insert_sql(self, fields, num_values): items_sql = "(%s)" % ", ".join(["%s"] * len(fields)) return "VALUES " + ", ".join([items_sql] * num_values) + class DatabaseWrapper(BaseDatabaseWrapper): vendor = 'mysql' operators = { diff --git a/django/db/backends/mysql/client.py b/django/db/backends/mysql/client.py index 1cf8ceef9c80d..8364c7b6e6d81 100644 --- a/django/db/backends/mysql/client.py +++ b/django/db/backends/mysql/client.py @@ -3,6 +3,7 @@ from django.db.backends import BaseDatabaseClient + class DatabaseClient(BaseDatabaseClient): executable_name = 'mysql' @@ -37,4 +38,3 @@ def runshell(self): sys.exit(os.system(" ".join(args))) else: os.execvp(self.executable_name, args) - diff --git a/django/db/backends/mysql/compiler.py b/django/db/backends/mysql/compiler.py index 4e033e3d931e0..b7d1d7b98d56b 100644 --- a/django/db/backends/mysql/compiler.py +++ b/django/db/backends/mysql/compiler.py @@ -22,20 +22,26 @@ def as_subquery_condition(self, alias, columns, qn): sql, params = self.as_sql() return '(%s) IN (%s)' % (', '.join(['%s.%s' % (qn(alias), qn2(column)) for column in columns]), sql), params + class SQLInsertCompiler(compiler.SQLInsertCompiler, SQLCompiler): pass + class SQLDeleteCompiler(compiler.SQLDeleteCompiler, SQLCompiler): pass + class SQLUpdateCompiler(compiler.SQLUpdateCompiler, SQLCompiler): pass + class SQLAggregateCompiler(compiler.SQLAggregateCompiler, SQLCompiler): pass + class SQLDateCompiler(compiler.SQLDateCompiler, SQLCompiler): pass + class SQLDateTimeCompiler(compiler.SQLDateTimeCompiler, SQLCompiler): pass diff --git a/django/db/backends/mysql/creation.py b/django/db/backends/mysql/creation.py index 3a57c29479ad2..b59b225f4f8cf 100644 --- a/django/db/backends/mysql/creation.py +++ b/django/db/backends/mysql/creation.py @@ -1,34 +1,35 @@ from django.db.backends.creation import BaseDatabaseCreation + class DatabaseCreation(BaseDatabaseCreation): # This dictionary maps Field objects to their associated MySQL column # types, as strings. Column-type strings can contain format strings; they'll # be interpolated against the values of Field.__dict__ before being output. # If a column type is set to None, it won't be included in the output. data_types = { - 'AutoField': 'integer AUTO_INCREMENT', - 'BinaryField': 'longblob', - 'BooleanField': 'bool', - 'CharField': 'varchar(%(max_length)s)', + 'AutoField': 'integer AUTO_INCREMENT', + 'BinaryField': 'longblob', + 'BooleanField': 'bool', + 'CharField': 'varchar(%(max_length)s)', 'CommaSeparatedIntegerField': 'varchar(%(max_length)s)', - 'DateField': 'date', - 'DateTimeField': 'datetime', - 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', - 'FileField': 'varchar(%(max_length)s)', - 'FilePathField': 'varchar(%(max_length)s)', - 'FloatField': 'double precision', - 'IntegerField': 'integer', - 'BigIntegerField': 'bigint', - 'IPAddressField': 'char(15)', + 'DateField': 'date', + 'DateTimeField': 'datetime', + 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', + 'FileField': 'varchar(%(max_length)s)', + 'FilePathField': 'varchar(%(max_length)s)', + 'FloatField': 'double precision', + 'IntegerField': 'integer', + 'BigIntegerField': 'bigint', + 'IPAddressField': 'char(15)', 'GenericIPAddressField': 'char(39)', - 'NullBooleanField': 'bool', - 'OneToOneField': 'integer', + 'NullBooleanField': 'bool', + 'OneToOneField': 'integer', 'PositiveIntegerField': 'integer UNSIGNED', 'PositiveSmallIntegerField': 'smallint UNSIGNED', - 'SlugField': 'varchar(%(max_length)s)', + 'SlugField': 'varchar(%(max_length)s)', 'SmallIntegerField': 'smallint', - 'TextField': 'longtext', - 'TimeField': 'time', + 'TextField': 'longtext', + 'TimeField': 'time', } def sql_table_creation_suffix(self): diff --git a/django/db/backends/mysql/introspection.py b/django/db/backends/mysql/introspection.py index 548877e8e63a6..ec9f3e99f80f1 100644 --- a/django/db/backends/mysql/introspection.py +++ b/django/db/backends/mysql/introspection.py @@ -7,6 +7,7 @@ foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)") + class DatabaseIntrospection(BaseDatabaseIntrospection): data_types_reverse = { FIELD_TYPE.BLOB: 'TextField', @@ -116,4 +117,3 @@ def get_indexes(self, cursor, table_name): continue indexes[row[4]] = {'primary_key': (row[2] == 'PRIMARY'), 'unique': not bool(row[1])} return indexes - diff --git a/django/db/backends/mysql/validation.py b/django/db/backends/mysql/validation.py index 2ce957cce7655..17b7cde75661d 100644 --- a/django/db/backends/mysql/validation.py +++ b/django/db/backends/mysql/validation.py @@ -1,5 +1,6 @@ from django.db.backends import BaseDatabaseValidation + class DatabaseValidation(BaseDatabaseValidation): def validate_field(self, errors, opts, f): """ diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py index 5e2b763f52ec1..04e70cfdb812e 100644 --- a/django/db/backends/oracle/base.py +++ b/django/db/backends/oracle/base.py @@ -7,11 +7,12 @@ import decimal import re +import platform import sys import warnings + def _setup_environment(environ): - import platform # Cygwin requires some special voodoo to set the environment variables # properly so that Oracle will see them. if platform.system().upper().startswith('CYGWIN'): @@ -90,6 +91,7 @@ class DatabaseFeatures(BaseDatabaseFeatures): supports_tablespaces = True supports_sequence_reset = False + class DatabaseOperations(BaseDatabaseOperations): compiler_module = "django.db.backends.oracle.compiler" @@ -308,7 +310,7 @@ def quote_name(self, name): # Oracle puts the query text into a (query % args) construct, so % signs # in names need to be escaped. The '%%' will be collapsed back to '%' at # that stage so we aren't really making the name longer here. - name = name.replace('%','%%') + name = name.replace('%', '%%') return name.upper() def random_function_sql(self): @@ -758,7 +760,7 @@ def __init__(self, connection): def _format_params(self, params): try: - return dict((k,OracleParam(v, self, True)) for k,v in params.items()) + return dict((k, OracleParam(v, self, True)) for k, v in params.items()) except AttributeError: return tuple([OracleParam(p, self, True) for p in params]) @@ -778,12 +780,12 @@ def _guess_input_sizes(self, params_list): for i, value in enumerate(params): if value.input_size: sizes[i] = value.input_size - self.setinputsizes(*sizes) + self.setinputsizes(*sizes) def _param_generator(self, params): # Try dict handling; if that fails, treat as sequence if hasattr(params, 'items'): - return dict((k, v.force_bytes) for k,v in params.items()) + return dict((k, v.force_bytes) for k, v in params.items()) else: return [p.force_bytes for p in params] @@ -799,14 +801,14 @@ def _fix_for_params(self, query, params): query = convert_unicode(query, self.charset) elif hasattr(params, 'keys'): # Handle params as dict - args = dict((k, ":%s"%k) for k in params.keys()) + args = dict((k, ":%s" % k) for k in params.keys()) query = convert_unicode(query % args, self.charset) else: # Handle params as sequence args = [(':arg%d' % i) for i in range(len(params))] query = convert_unicode(query % tuple(args), self.charset) return query, self._format_params(params) - + def execute(self, query, params=None): query, params = self._fix_for_params(query, params) self._guess_input_sizes([params]) @@ -825,9 +827,9 @@ def executemany(self, query, params=None): # uniform treatment for sequences and iterables params_iter = iter(params) query, firstparams = self._fix_for_params(query, next(params_iter)) - # we build a list of formatted params; as we're going to traverse it + # we build a list of formatted params; as we're going to traverse it # more than once, we can't make it lazy by using a generator - formatted = [firstparams]+[self._format_params(p) for p in params_iter] + formatted = [firstparams] + [self._format_params(p) for p in params_iter] self._guess_input_sizes(formatted) try: return self.cursor.executemany(query, diff --git a/django/db/backends/oracle/client.py b/django/db/backends/oracle/client.py index ccc64ebffc701..ac6b79041f761 100644 --- a/django/db/backends/oracle/client.py +++ b/django/db/backends/oracle/client.py @@ -3,6 +3,7 @@ from django.db.backends import BaseDatabaseClient + class DatabaseClient(BaseDatabaseClient): executable_name = 'sqlplus' @@ -13,4 +14,3 @@ def runshell(self): sys.exit(os.system(" ".join(args))) else: os.execvp(self.executable_name, args) - diff --git a/django/db/backends/oracle/compiler.py b/django/db/backends/oracle/compiler.py index cbee27951c9b0..d2d4cd3ac9db8 100644 --- a/django/db/backends/oracle/compiler.py +++ b/django/db/backends/oracle/compiler.py @@ -60,17 +60,22 @@ def as_sql(self, with_limits=True, with_col_aliases=False): class SQLInsertCompiler(compiler.SQLInsertCompiler, SQLCompiler): pass + class SQLDeleteCompiler(compiler.SQLDeleteCompiler, SQLCompiler): pass + class SQLUpdateCompiler(compiler.SQLUpdateCompiler, SQLCompiler): pass + class SQLAggregateCompiler(compiler.SQLAggregateCompiler, SQLCompiler): pass + class SQLDateCompiler(compiler.SQLDateCompiler, SQLCompiler): pass + class SQLDateTimeCompiler(compiler.SQLDateTimeCompiler, SQLCompiler): pass diff --git a/django/db/backends/oracle/creation.py b/django/db/backends/oracle/creation.py index c7808e4849697..55f6ee4d7e4a2 100644 --- a/django/db/backends/oracle/creation.py +++ b/django/db/backends/oracle/creation.py @@ -5,9 +5,11 @@ from django.db.backends.creation import BaseDatabaseCreation from django.utils.six.moves import input + TEST_DATABASE_PREFIX = 'test_' PASSWORD = 'Im_a_lumberjack' + class DatabaseCreation(BaseDatabaseCreation): # This dictionary maps Field objects to their associated Oracle column # types, as strings. Column-type strings can contain format strings; they'll @@ -18,30 +20,30 @@ class DatabaseCreation(BaseDatabaseCreation): # output (the "qn_" prefix is stripped before the lookup is performed. data_types = { - 'AutoField': 'NUMBER(11)', - 'BinaryField': 'BLOB', - 'BooleanField': 'NUMBER(1) CHECK (%(qn_column)s IN (0,1))', - 'CharField': 'NVARCHAR2(%(max_length)s)', - 'CommaSeparatedIntegerField': 'VARCHAR2(%(max_length)s)', - 'DateField': 'DATE', - 'DateTimeField': 'TIMESTAMP', - 'DecimalField': 'NUMBER(%(max_digits)s, %(decimal_places)s)', - 'FileField': 'NVARCHAR2(%(max_length)s)', - 'FilePathField': 'NVARCHAR2(%(max_length)s)', - 'FloatField': 'DOUBLE PRECISION', - 'IntegerField': 'NUMBER(11)', - 'BigIntegerField': 'NUMBER(19)', - 'IPAddressField': 'VARCHAR2(15)', - 'GenericIPAddressField': 'VARCHAR2(39)', - 'NullBooleanField': 'NUMBER(1) CHECK ((%(qn_column)s IN (0,1)) OR (%(qn_column)s IS NULL))', - 'OneToOneField': 'NUMBER(11)', - 'PositiveIntegerField': 'NUMBER(11) CHECK (%(qn_column)s >= 0)', - 'PositiveSmallIntegerField': 'NUMBER(11) CHECK (%(qn_column)s >= 0)', - 'SlugField': 'NVARCHAR2(%(max_length)s)', - 'SmallIntegerField': 'NUMBER(11)', - 'TextField': 'NCLOB', - 'TimeField': 'TIMESTAMP', - 'URLField': 'VARCHAR2(%(max_length)s)', + 'AutoField': 'NUMBER(11)', + 'BinaryField': 'BLOB', + 'BooleanField': 'NUMBER(1) CHECK (%(qn_column)s IN (0,1))', + 'CharField': 'NVARCHAR2(%(max_length)s)', + 'CommaSeparatedIntegerField': 'VARCHAR2(%(max_length)s)', + 'DateField': 'DATE', + 'DateTimeField': 'TIMESTAMP', + 'DecimalField': 'NUMBER(%(max_digits)s, %(decimal_places)s)', + 'FileField': 'NVARCHAR2(%(max_length)s)', + 'FilePathField': 'NVARCHAR2(%(max_length)s)', + 'FloatField': 'DOUBLE PRECISION', + 'IntegerField': 'NUMBER(11)', + 'BigIntegerField': 'NUMBER(19)', + 'IPAddressField': 'VARCHAR2(15)', + 'GenericIPAddressField': 'VARCHAR2(39)', + 'NullBooleanField': 'NUMBER(1) CHECK ((%(qn_column)s IN (0,1)) OR (%(qn_column)s IS NULL))', + 'OneToOneField': 'NUMBER(11)', + 'PositiveIntegerField': 'NUMBER(11) CHECK (%(qn_column)s >= 0)', + 'PositiveSmallIntegerField': 'NUMBER(11) CHECK (%(qn_column)s >= 0)', + 'SlugField': 'NVARCHAR2(%(max_length)s)', + 'SmallIntegerField': 'NUMBER(11)', + 'TextField': 'NCLOB', + 'TimeField': 'TIMESTAMP', + 'URLField': 'VARCHAR2(%(max_length)s)', } def __init__(self, connection): @@ -183,7 +185,7 @@ def _execute_test_db_destruction(self, cursor, parameters, verbosity): statements = [ 'DROP TABLESPACE %(tblspace)s INCLUDING CONTENTS AND DATAFILES CASCADE CONSTRAINTS', 'DROP TABLESPACE %(tblspace_temp)s INCLUDING CONTENTS AND DATAFILES CASCADE CONSTRAINTS', - ] + ] self._execute_statements(cursor, statements, parameters, verbosity) def _destroy_test_user(self, cursor, parameters, verbosity): diff --git a/django/db/backends/oracle/introspection.py b/django/db/backends/oracle/introspection.py index 361308a62cb1b..a2fad92509268 100644 --- a/django/db/backends/oracle/introspection.py +++ b/django/db/backends/oracle/introspection.py @@ -1,10 +1,13 @@ +import re + +import cx_Oracle + from django.db.backends import BaseDatabaseIntrospection, FieldInfo from django.utils.encoding import force_text -import cx_Oracle -import re foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)") + class DatabaseIntrospection(BaseDatabaseIntrospection): # Maps type objects to Django Field types. data_types_reverse = { @@ -95,11 +98,11 @@ def get_key_columns(self, cursor, table_name): SELECT ccol.column_name, rcol.table_name AS referenced_table, rcol.column_name AS referenced_column FROM user_constraints c JOIN user_cons_columns ccol - ON ccol.constraint_name = c.constraint_name + ON ccol.constraint_name = c.constraint_name JOIN user_cons_columns rcol - ON rcol.constraint_name = c.r_constraint_name - WHERE c.table_name = %s AND c.constraint_type = 'R'""" , [table_name.upper()]) - return [tuple(cell.lower() for cell in row) + ON rcol.constraint_name = c.r_constraint_name + WHERE c.table_name = %s AND c.constraint_type = 'R'""", [table_name.upper()]) + return [tuple(cell.lower() for cell in row) for row in cursor.fetchall()] def get_indexes(self, cursor, table_name): diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py index 6ed2cfcc7cdc7..e6760655782da 100644 --- a/django/db/backends/postgresql_psycopg2/base.py +++ b/django/db/backends/postgresql_psycopg2/base.py @@ -6,7 +6,9 @@ import logging import sys -from django.db.backends import * +from django.conf import settings +from django.db.backends import (BaseDatabaseFeatures, BaseDatabaseWrapper, + BaseDatabaseValidation) from django.db.backends.postgresql_psycopg2.operations import DatabaseOperations from django.db.backends.postgresql_psycopg2.client import DatabaseClient from django.db.backends.postgresql_psycopg2.creation import DatabaseCreation @@ -33,11 +35,13 @@ logger = logging.getLogger('django.db.backends') + def utc_tzinfo_factory(offset): if offset != 0: raise AssertionError("database connection isn't set to UTC") return utc + class DatabaseFeatures(BaseDatabaseFeatures): needs_datetime_string_cast = False can_return_id_from_insert = True @@ -52,6 +56,7 @@ class DatabaseFeatures(BaseDatabaseFeatures): supports_transactions = True can_distinct_on_fields = True + class DatabaseWrapper(BaseDatabaseWrapper): vendor = 'postgresql' operators = { @@ -132,7 +137,8 @@ def init_connection_state(self): # Set the time zone in autocommit mode (see #17062) self.set_autocommit(True) self.connection.cursor().execute( - self.ops.set_time_zone_sql(), [tz]) + self.ops.set_time_zone_sql(), [tz] + ) self.connection.set_isolation_level(self.isolation_level) def create_cursor(self): diff --git a/django/db/backends/postgresql_psycopg2/client.py b/django/db/backends/postgresql_psycopg2/client.py index a5c02969ea8b3..23ac9f2975dcf 100644 --- a/django/db/backends/postgresql_psycopg2/client.py +++ b/django/db/backends/postgresql_psycopg2/client.py @@ -3,6 +3,7 @@ from django.db.backends import BaseDatabaseClient + class DatabaseClient(BaseDatabaseClient): executable_name = 'psql' @@ -20,4 +21,3 @@ def runshell(self): sys.exit(os.system(" ".join(args))) else: os.execvp(self.executable_name, args) - diff --git a/django/db/backends/postgresql_psycopg2/creation.py b/django/db/backends/postgresql_psycopg2/creation.py index d4260e05c4ad8..cbf901555d528 100644 --- a/django/db/backends/postgresql_psycopg2/creation.py +++ b/django/db/backends/postgresql_psycopg2/creation.py @@ -8,29 +8,29 @@ class DatabaseCreation(BaseDatabaseCreation): # be interpolated against the values of Field.__dict__ before being output. # If a column type is set to None, it won't be included in the output. data_types = { - 'AutoField': 'serial', - 'BinaryField': 'bytea', - 'BooleanField': 'boolean', - 'CharField': 'varchar(%(max_length)s)', + 'AutoField': 'serial', + 'BinaryField': 'bytea', + 'BooleanField': 'boolean', + 'CharField': 'varchar(%(max_length)s)', 'CommaSeparatedIntegerField': 'varchar(%(max_length)s)', - 'DateField': 'date', - 'DateTimeField': 'timestamp with time zone', - 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', - 'FileField': 'varchar(%(max_length)s)', - 'FilePathField': 'varchar(%(max_length)s)', - 'FloatField': 'double precision', - 'IntegerField': 'integer', - 'BigIntegerField': 'bigint', - 'IPAddressField': 'inet', + 'DateField': 'date', + 'DateTimeField': 'timestamp with time zone', + 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', + 'FileField': 'varchar(%(max_length)s)', + 'FilePathField': 'varchar(%(max_length)s)', + 'FloatField': 'double precision', + 'IntegerField': 'integer', + 'BigIntegerField': 'bigint', + 'IPAddressField': 'inet', 'GenericIPAddressField': 'inet', - 'NullBooleanField': 'boolean', - 'OneToOneField': 'integer', + 'NullBooleanField': 'boolean', + 'OneToOneField': 'integer', 'PositiveIntegerField': 'integer CHECK ("%(column)s" >= 0)', 'PositiveSmallIntegerField': 'smallint CHECK ("%(column)s" >= 0)', - 'SlugField': 'varchar(%(max_length)s)', + 'SlugField': 'varchar(%(max_length)s)', 'SmallIntegerField': 'smallint', - 'TextField': 'text', - 'TimeField': 'time', + 'TextField': 'text', + 'TimeField': 'time', } def sql_table_creation_suffix(self): @@ -54,7 +54,7 @@ def sql_indexes_for_field(self, model, f, style): def get_index_sql(index_name, opclass=''): return (style.SQL_KEYWORD('CREATE INDEX') + ' ' + - style.SQL_TABLE(qn(truncate_name(index_name,self.connection.ops.max_name_length()))) + ' ' + + style.SQL_TABLE(qn(truncate_name(index_name, self.connection.ops.max_name_length()))) + ' ' + style.SQL_KEYWORD('ON') + ' ' + style.SQL_TABLE(qn(db_table)) + ' ' + "(%s%s)" % (style.SQL_FIELD(qn(f.column)), opclass) + diff --git a/django/db/backends/postgresql_psycopg2/introspection.py b/django/db/backends/postgresql_psycopg2/introspection.py index c334b9d6d0b0e..0ebd3c1ed5710 100644 --- a/django/db/backends/postgresql_psycopg2/introspection.py +++ b/django/db/backends/postgresql_psycopg2/introspection.py @@ -25,7 +25,7 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): 1266: 'TimeField', 1700: 'DecimalField', } - + def get_table_list(self, cursor): "Returns a list of table names in the current database." cursor.execute(""" @@ -47,7 +47,7 @@ def get_table_description(self, cursor, table_name): WHERE table_name = %s""", [table_name]) null_map = dict(cursor.fetchall()) cursor.execute("SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name)) - return [FieldInfo(*((force_text(line[0]),) + line[1:6] + (null_map[force_text(line[0])]=='YES',))) + return [FieldInfo(*((force_text(line[0]),) + line[1:6] + (null_map[force_text(line[0])] == 'YES',))) for line in cursor.description] def get_relations(self, cursor, table_name): @@ -81,7 +81,7 @@ def get_key_columns(self, cursor, table_name): ON ccu.constraint_catalog = tc.constraint_catalog AND ccu.constraint_schema = tc.constraint_schema AND ccu.constraint_name = tc.constraint_name - WHERE kcu.table_name = %s AND tc.constraint_type = 'FOREIGN KEY'""" , [table_name]) + WHERE kcu.table_name = %s AND tc.constraint_type = 'FOREIGN KEY'""", [table_name]) key_columns.extend(cursor.fetchall()) return key_columns diff --git a/django/db/backends/postgresql_psycopg2/operations.py b/django/db/backends/postgresql_psycopg2/operations.py index c5aab8469307d..cc78ffe4491b6 100644 --- a/django/db/backends/postgresql_psycopg2/operations.py +++ b/django/db/backends/postgresql_psycopg2/operations.py @@ -135,7 +135,7 @@ def sequence_reset_by_name_sql(self, style, sequences): # This will be the case if it's an m2m using an autogenerated # intermediate table (see BaseDatabaseIntrospection.sequence_list) column_name = 'id' - sql.append("%s setval(pg_get_serial_sequence('%s','%s'), 1, false);" % \ + sql.append("%s setval(pg_get_serial_sequence('%s','%s'), 1, false);" % (style.SQL_KEYWORD('SELECT'), style.SQL_TABLE(self.quote_name(table_name)), style.SQL_FIELD(column_name)) @@ -161,7 +161,7 @@ def sequence_reset_sql(self, style, model_list): for f in model._meta.local_fields: if isinstance(f, models.AutoField): - output.append("%s setval(pg_get_serial_sequence('%s','%s'), coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \ + output.append("%s setval(pg_get_serial_sequence('%s','%s'), coalesce(max(%s), 1), max(%s) %s null) %s %s;" % (style.SQL_KEYWORD('SELECT'), style.SQL_TABLE(qn(model._meta.db_table)), style.SQL_FIELD(f.column), @@ -173,7 +173,7 @@ def sequence_reset_sql(self, style, model_list): break # Only one AutoField is allowed per model, so don't bother continuing. for f in model._meta.many_to_many: if not f.rel.through: - output.append("%s setval(pg_get_serial_sequence('%s','%s'), coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \ + output.append("%s setval(pg_get_serial_sequence('%s','%s'), coalesce(max(%s), 1), max(%s) %s null) %s %s;" % (style.SQL_KEYWORD('SELECT'), style.SQL_TABLE(qn(f.m2m_db_table())), style.SQL_FIELD('id'), diff --git a/django/db/backends/postgresql_psycopg2/version.py b/django/db/backends/postgresql_psycopg2/version.py index 8ef516704eaa1..dae94f2dacd44 100644 --- a/django/db/backends/postgresql_psycopg2/version.py +++ b/django/db/backends/postgresql_psycopg2/version.py @@ -19,7 +19,8 @@ def _parse_version(text): try: return int(major) * 10000 + int(major2) * 100 + int(minor) except (ValueError, TypeError): - return int(major) * 10000 + int(major2) * 100 + return int(major) * 10000 + int(major2) * 100 + def get_version(connection): """ diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py index 4345790b06cee..dc7db2fceb08b 100644 --- a/django/db/backends/sqlite3/base.py +++ b/django/db/backends/sqlite3/base.py @@ -11,8 +11,10 @@ import warnings import re +from django.conf import settings from django.db import utils -from django.db.backends import * +from django.db.backends import (util, BaseDatabaseFeatures, + BaseDatabaseOperations, BaseDatabaseWrapper, BaseDatabaseValidation) from django.db.backends.sqlite3.client import DatabaseClient from django.db.backends.sqlite3.creation import DatabaseCreation from django.db.backends.sqlite3.introspection import DatabaseIntrospection @@ -42,6 +44,7 @@ DatabaseError = Database.DatabaseError IntegrityError = Database.IntegrityError + def parse_datetime_with_timezone_support(value): dt = parse_datetime(value) # Confirm that dt is naive before overwriting its tzinfo. @@ -49,6 +52,7 @@ def parse_datetime_with_timezone_support(value): dt = dt.replace(tzinfo=timezone.utc) return dt + def adapt_datetime_with_timezone_support(value): # Equivalent to DateTimeField.get_db_prep_value. Used only by raw SQL. if settings.USE_TZ: @@ -61,6 +65,7 @@ def adapt_datetime_with_timezone_support(value): value = value.astimezone(timezone.utc).replace(tzinfo=None) return value.isoformat(str(" ")) + def decoder(conv_func): """ The Python sqlite3 interface returns always byte strings. This function converts the received value to a regular string before @@ -81,6 +86,7 @@ def decoder(conv_func): Database.register_adapter(str, lambda s: s.decode('utf-8')) Database.register_adapter(SafeBytes, lambda s: s.decode('utf-8')) + class DatabaseFeatures(BaseDatabaseFeatures): # SQLite cannot handle us only partially reading from a cursor's result set # and then writing the same rows to the database in another cursor. This @@ -124,6 +130,7 @@ def supports_stddev(self): def has_zoneinfo_database(self): return pytz is not None + class DatabaseOperations(BaseDatabaseOperations): def bulk_batch_size(self, fields, objs): """ @@ -272,6 +279,7 @@ def bulk_insert_sql(self, fields, num_values): res.extend(["UNION ALL SELECT %s" % ", ".join(["%s"] * len(fields))] * (num_values - 1)) return " ".join(res) + class DatabaseWrapper(BaseDatabaseWrapper): vendor = 'sqlite' # SQLite requires LIKE statements to include an ESCAPE clause if the value @@ -426,6 +434,7 @@ def _start_transaction_under_autocommit(self): FORMAT_QMARK_REGEX = re.compile(r'(?