summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorMichael Manfre <mmanfre@gmail.com>2014-01-09 10:05:15 -0500
committerMichael Manfre <mmanfre@gmail.com>2014-02-02 12:47:21 -0500
commit3ffeb931869cc68a8e0916219702ee282afc6e9d (patch)
treef24020307dd5b529989329bfabfc95effcecffe8 /django
parent0837eacc4e1fa7916e48135e8ba43f54a7a64997 (diff)
Ensure cursors are closed when no longer needed.
This commit touchs various parts of the code base and test framework. Any found usage of opening a cursor for the sake of initializing a connection has been replaced with 'ensure_connection()'.
Diffstat (limited to 'django')
-rw-r--r--django/contrib/gis/db/backends/postgis/creation.py14
-rw-r--r--django/contrib/gis/db/backends/spatialite/creation.py5
-rw-r--r--django/contrib/sites/management.py6
-rw-r--r--django/core/cache/backends/db.py129
-rw-r--r--django/core/management/commands/createcachetable.py18
-rw-r--r--django/core/management/commands/flush.py6
-rw-r--r--django/core/management/commands/inspectdb.py184
-rw-r--r--django/core/management/commands/loaddata.py7
-rw-r--r--django/core/management/commands/migrate.py181
-rw-r--r--django/core/management/sql.py55
-rw-r--r--django/db/backends/__init__.py30
-rw-r--r--django/db/backends/creation.py71
-rw-r--r--django/db/backends/mysql/base.py37
-rw-r--r--django/db/backends/oracle/base.py4
-rw-r--r--django/db/backends/postgresql_psycopg2/base.py6
-rw-r--r--django/db/backends/postgresql_psycopg2/version.py6
-rw-r--r--django/db/backends/schema.py8
-rw-r--r--django/db/backends/sqlite3/base.py16
-rw-r--r--django/db/models/query.py89
-rw-r--r--django/db/models/sql/compiler.py21
20 files changed, 454 insertions, 439 deletions
diff --git a/django/contrib/gis/db/backends/postgis/creation.py b/django/contrib/gis/db/backends/postgis/creation.py
index 51ac197b8e..82be18cb65 100644
--- a/django/contrib/gis/db/backends/postgis/creation.py
+++ b/django/contrib/gis/db/backends/postgis/creation.py
@@ -11,10 +11,10 @@ class PostGISCreation(DatabaseCreation):
@cached_property
def template_postgis(self):
template_postgis = getattr(settings, 'POSTGIS_TEMPLATE', 'template_postgis')
- cursor = self.connection.cursor()
- cursor.execute('SELECT 1 FROM pg_database WHERE datname = %s LIMIT 1;', (template_postgis,))
- if cursor.fetchone():
- return template_postgis
+ with self.connection.cursor() as cursor:
+ cursor.execute('SELECT 1 FROM pg_database WHERE datname = %s LIMIT 1;', (template_postgis,))
+ if cursor.fetchone():
+ return template_postgis
return None
def sql_indexes_for_field(self, model, f, style):
@@ -88,8 +88,8 @@ class PostGISCreation(DatabaseCreation):
# Connect to the test database in order to create the postgis extension
self.connection.close()
self.connection.settings_dict["NAME"] = test_database_name
- cursor = self.connection.cursor()
- cursor.execute("CREATE EXTENSION IF NOT EXISTS postgis")
- cursor.connection.commit()
+ with self.connection.cursor() as cursor:
+ cursor.execute("CREATE EXTENSION IF NOT EXISTS postgis")
+ cursor.connection.commit()
return test_database_name
diff --git a/django/contrib/gis/db/backends/spatialite/creation.py b/django/contrib/gis/db/backends/spatialite/creation.py
index 521985259e..06f105d563 100644
--- a/django/contrib/gis/db/backends/spatialite/creation.py
+++ b/django/contrib/gis/db/backends/spatialite/creation.py
@@ -55,9 +55,8 @@ class SpatiaLiteCreation(DatabaseCreation):
call_command('createcachetable', database=self.connection.alias)
- # Get a cursor (even though we don't need one yet). This has
- # the side effect of initializing the test database.
- self.connection.cursor()
+ # Ensure a connection for the side effect of initializing the test database.
+ self.connection.ensure_connection()
return test_database_name
diff --git a/django/contrib/sites/management.py b/django/contrib/sites/management.py
index e7624e75cf..8353a6f496 100644
--- a/django/contrib/sites/management.py
+++ b/django/contrib/sites/management.py
@@ -33,9 +33,9 @@ def create_default_site(app_config, verbosity=2, interactive=True, db=DEFAULT_DB
if sequence_sql:
if verbosity >= 2:
print("Resetting sequence")
- cursor = connections[db].cursor()
- for command in sequence_sql:
- cursor.execute(command)
+ with connections[db].cursor() as cursor:
+ for command in sequence_sql:
+ cursor.execute(command)
Site.objects.clear_cache()
diff --git a/django/core/cache/backends/db.py b/django/core/cache/backends/db.py
index a21777aaba..959095026b 100644
--- a/django/core/cache/backends/db.py
+++ b/django/core/cache/backends/db.py
@@ -59,11 +59,11 @@ class DatabaseCache(BaseDatabaseCache):
self.validate_key(key)
db = router.db_for_read(self.cache_model_class)
table = connections[db].ops.quote_name(self._table)
- cursor = connections[db].cursor()
- cursor.execute("SELECT cache_key, value, expires FROM %s "
- "WHERE cache_key = %%s" % table, [key])
- row = cursor.fetchone()
+ with connections[db].cursor() as cursor:
+ cursor.execute("SELECT cache_key, value, expires FROM %s "
+ "WHERE cache_key = %%s" % table, [key])
+ row = cursor.fetchone()
if row is None:
return default
now = timezone.now()
@@ -75,9 +75,9 @@ class DatabaseCache(BaseDatabaseCache):
expires = typecast_timestamp(str(expires))
if expires < now:
db = router.db_for_write(self.cache_model_class)
- cursor = connections[db].cursor()
- cursor.execute("DELETE FROM %s "
- "WHERE cache_key = %%s" % table, [key])
+ with connections[db].cursor() as cursor:
+ cursor.execute("DELETE FROM %s "
+ "WHERE cache_key = %%s" % table, [key])
return default
value = connections[db].ops.process_clob(row[1])
return pickle.loads(base64.b64decode(force_bytes(value)))
@@ -96,55 +96,55 @@ class DatabaseCache(BaseDatabaseCache):
timeout = self.get_backend_timeout(timeout)
db = router.db_for_write(self.cache_model_class)
table = connections[db].ops.quote_name(self._table)
- cursor = connections[db].cursor()
- cursor.execute("SELECT COUNT(*) FROM %s" % table)
- num = cursor.fetchone()[0]
- now = timezone.now()
- now = now.replace(microsecond=0)
- if timeout is None:
- exp = datetime.max
- elif settings.USE_TZ:
- exp = datetime.utcfromtimestamp(timeout)
- else:
- exp = datetime.fromtimestamp(timeout)
- exp = exp.replace(microsecond=0)
- if num > self._max_entries:
- self._cull(db, cursor, now)
- pickled = pickle.dumps(value, pickle.HIGHEST_PROTOCOL)
- b64encoded = base64.b64encode(pickled)
- # The DB column is expecting a string, so make sure the value is a
- # string, not bytes. Refs #19274.
- if six.PY3:
- b64encoded = b64encoded.decode('latin1')
- try:
- # Note: typecasting for datetimes is needed by some 3rd party
- # database backends. All core backends work without typecasting,
- # so be careful about changes here - test suite will NOT pick
- # regressions.
- with transaction.atomic(using=db):
- cursor.execute("SELECT cache_key, expires FROM %s "
- "WHERE cache_key = %%s" % table, [key])
- result = cursor.fetchone()
- if result:
- current_expires = result[1]
- if (connections[db].features.needs_datetime_string_cast and not
- isinstance(current_expires, datetime)):
- current_expires = typecast_timestamp(str(current_expires))
- exp = connections[db].ops.value_to_db_datetime(exp)
- if result and (mode == 'set' or (mode == 'add' and current_expires < now)):
- cursor.execute("UPDATE %s SET value = %%s, expires = %%s "
- "WHERE cache_key = %%s" % table,
- [b64encoded, exp, key])
- else:
- cursor.execute("INSERT INTO %s (cache_key, value, expires) "
- "VALUES (%%s, %%s, %%s)" % table,
- [key, b64encoded, exp])
- except DatabaseError:
- # To be threadsafe, updates/inserts are allowed to fail silently
- return False
- else:
- return True
+ with connections[db].cursor() as cursor:
+ cursor.execute("SELECT COUNT(*) FROM %s" % table)
+ num = cursor.fetchone()[0]
+ now = timezone.now()
+ now = now.replace(microsecond=0)
+ if timeout is None:
+ exp = datetime.max
+ elif settings.USE_TZ:
+ exp = datetime.utcfromtimestamp(timeout)
+ else:
+ exp = datetime.fromtimestamp(timeout)
+ exp = exp.replace(microsecond=0)
+ if num > self._max_entries:
+ self._cull(db, cursor, now)
+ pickled = pickle.dumps(value, pickle.HIGHEST_PROTOCOL)
+ b64encoded = base64.b64encode(pickled)
+ # The DB column is expecting a string, so make sure the value is a
+ # string, not bytes. Refs #19274.
+ if six.PY3:
+ b64encoded = b64encoded.decode('latin1')
+ try:
+ # Note: typecasting for datetimes is needed by some 3rd party
+ # database backends. All core backends work without typecasting,
+ # so be careful about changes here - test suite will NOT pick
+ # regressions.
+ with transaction.atomic(using=db):
+ cursor.execute("SELECT cache_key, expires FROM %s "
+ "WHERE cache_key = %%s" % table, [key])
+ result = cursor.fetchone()
+ if result:
+ current_expires = result[1]
+ if (connections[db].features.needs_datetime_string_cast and not
+ isinstance(current_expires, datetime)):
+ current_expires = typecast_timestamp(str(current_expires))
+ exp = connections[db].ops.value_to_db_datetime(exp)
+ if result and (mode == 'set' or (mode == 'add' and current_expires < now)):
+ cursor.execute("UPDATE %s SET value = %%s, expires = %%s "
+ "WHERE cache_key = %%s" % table,
+ [b64encoded, exp, key])
+ else:
+ cursor.execute("INSERT INTO %s (cache_key, value, expires) "
+ "VALUES (%%s, %%s, %%s)" % table,
+ [key, b64encoded, exp])
+ except DatabaseError:
+ # To be threadsafe, updates/inserts are allowed to fail silently
+ return False
+ else:
+ return True
def delete(self, key, version=None):
key = self.make_key(key, version=version)
@@ -152,9 +152,9 @@ class DatabaseCache(BaseDatabaseCache):
db = router.db_for_write(self.cache_model_class)
table = connections[db].ops.quote_name(self._table)
- cursor = connections[db].cursor()
- cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % table, [key])
+ with connections[db].cursor() as cursor:
+ cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % table, [key])
def has_key(self, key, version=None):
key = self.make_key(key, version=version)
@@ -162,17 +162,18 @@ class DatabaseCache(BaseDatabaseCache):
db = router.db_for_read(self.cache_model_class)
table = connections[db].ops.quote_name(self._table)
- cursor = connections[db].cursor()
if settings.USE_TZ:
now = datetime.utcnow()
else:
now = datetime.now()
now = now.replace(microsecond=0)
- cursor.execute("SELECT cache_key FROM %s "
- "WHERE cache_key = %%s and expires > %%s" % table,
- [key, connections[db].ops.value_to_db_datetime(now)])
- return cursor.fetchone() is not None
+
+ with connections[db].cursor() as cursor:
+ cursor.execute("SELECT cache_key FROM %s "
+ "WHERE cache_key = %%s and expires > %%s" % table,
+ [key, connections[db].ops.value_to_db_datetime(now)])
+ return cursor.fetchone() is not None
def _cull(self, db, cursor, now):
if self._cull_frequency == 0:
@@ -197,8 +198,8 @@ class DatabaseCache(BaseDatabaseCache):
def clear(self):
db = router.db_for_write(self.cache_model_class)
table = connections[db].ops.quote_name(self._table)
- cursor = connections[db].cursor()
- cursor.execute('DELETE FROM %s' % table)
+ with connections[db].cursor() as cursor:
+ cursor.execute('DELETE FROM %s' % table)
# For backwards compatibility
diff --git a/django/core/management/commands/createcachetable.py b/django/core/management/commands/createcachetable.py
index 10506525fc..909a5d08c8 100644
--- a/django/core/management/commands/createcachetable.py
+++ b/django/core/management/commands/createcachetable.py
@@ -72,14 +72,14 @@ class Command(BaseCommand):
full_statement.append(' %s%s' % (line, ',' if i < len(table_output) - 1 else ''))
full_statement.append(');')
with transaction.commit_on_success_unless_managed():
- curs = connection.cursor()
- try:
- curs.execute("\n".join(full_statement))
- except DatabaseError as e:
- raise CommandError(
- "Cache table '%s' could not be created.\nThe error was: %s." %
- (tablename, force_text(e)))
- for statement in index_output:
- curs.execute(statement)
+ with connection.cursor() as curs:
+ try:
+ curs.execute("\n".join(full_statement))
+ except DatabaseError as e:
+ raise CommandError(
+ "Cache table '%s' could not be created.\nThe error was: %s." %
+ (tablename, force_text(e)))
+ for statement in index_output:
+ curs.execute(statement)
if self.verbosity > 1:
self.stdout.write("Cache table '%s' created." % tablename)
diff --git a/django/core/management/commands/flush.py b/django/core/management/commands/flush.py
index 4a3f7c2d8b..d99deb951e 100644
--- a/django/core/management/commands/flush.py
+++ b/django/core/management/commands/flush.py
@@ -64,9 +64,9 @@ Are you sure you want to do this?
if confirm == 'yes':
try:
with transaction.commit_on_success_unless_managed():
- cursor = connection.cursor()
- for sql in sql_list:
- cursor.execute(sql)
+ with connection.cursor() as cursor:
+ for sql in sql_list:
+ cursor.execute(sql)
except Exception as e:
new_msg = (
"Database %s couldn't be flushed. Possible reasons:\n"
diff --git a/django/core/management/commands/inspectdb.py b/django/core/management/commands/inspectdb.py
index 54fdad2001..4a51892e5a 100644
--- a/django/core/management/commands/inspectdb.py
+++ b/django/core/management/commands/inspectdb.py
@@ -37,108 +37,108 @@ class Command(NoArgsCommand):
table2model = lambda table_name: table_name.title().replace('_', '').replace(' ', '').replace('-', '')
strip_prefix = lambda s: s[1:] if s.startswith("u'") else s
- cursor = connection.cursor()
- yield "# This is an auto-generated Django model module."
- yield "# You'll have to do the following manually to clean this up:"
- yield "# * Rearrange models' order"
- yield "# * Make sure each model has one field with primary_key=True"
- yield "# * Remove `managed = False` lines for those models you wish to give write DB access"
- yield "# Feel free to rename the models, but don't rename db_table values or field names."
- yield "#"
- yield "# Also note: You'll have to insert the output of 'django-admin.py sqlcustom [app_label]'"
- yield "# into your database."
- yield "from __future__ import unicode_literals"
- yield ''
- yield 'from %s import models' % self.db_module
- known_models = []
- for table_name in connection.introspection.table_names(cursor):
- if table_name_filter is not None and callable(table_name_filter):
- if not table_name_filter(table_name):
- continue
+ with connection.cursor() as cursor:
+ yield "# This is an auto-generated Django model module."
+ yield "# You'll have to do the following manually to clean this up:"
+ yield "# * Rearrange models' order"
+ yield "# * Make sure each model has one field with primary_key=True"
+ yield "# * Remove `managed = False` lines for those models you wish to give write DB access"
+ yield "# Feel free to rename the models, but don't rename db_table values or field names."
+ yield "#"
+ yield "# Also note: You'll have to insert the output of 'django-admin.py sqlcustom [app_label]'"
+ yield "# into your database."
+ yield "from __future__ import unicode_literals"
yield ''
- yield ''
- yield 'class %s(models.Model):' % table2model(table_name)
- known_models.append(table2model(table_name))
- try:
- relations = connection.introspection.get_relations(cursor, table_name)
- except NotImplementedError:
- relations = {}
- try:
- indexes = connection.introspection.get_indexes(cursor, table_name)
- except NotImplementedError:
- indexes = {}
- used_column_names = [] # Holds column names used in the table so far
- for i, row in enumerate(connection.introspection.get_table_description(cursor, table_name)):
- comment_notes = [] # Holds Field notes, to be displayed in a Python comment.
- extra_params = OrderedDict() # Holds Field parameters such as 'db_column'.
- column_name = row[0]
- is_relation = i in relations
+ yield 'from %s import models' % self.db_module
+ known_models = []
+ for table_name in connection.introspection.table_names(cursor):
+ if table_name_filter is not None and callable(table_name_filter):
+ if not table_name_filter(table_name):
+ continue
+ yield ''
+ yield ''
+ yield 'class %s(models.Model):' % table2model(table_name)
+ known_models.append(table2model(table_name))
+ try:
+ relations = connection.introspection.get_relations(cursor, table_name)
+ except NotImplementedError:
+ relations = {}
+ try:
+ indexes = connection.introspection.get_indexes(cursor, table_name)
+ except NotImplementedError:
+ indexes = {}
+ used_column_names = [] # Holds column names used in the table so far
+ for i, row in enumerate(connection.introspection.get_table_description(cursor, table_name)):
+ comment_notes = [] # Holds Field notes, to be displayed in a Python comment.
+ extra_params = OrderedDict() # Holds Field parameters such as 'db_column'.
+ column_name = row[0]
+ is_relation = i in relations
- att_name, params, notes = self.normalize_col_name(
- column_name, used_column_names, is_relation)
- extra_params.update(params)
- comment_notes.extend(notes)
+ att_name, params, notes = self.normalize_col_name(
+ column_name, used_column_names, is_relation)
+ extra_params.update(params)
+ comment_notes.extend(notes)
- used_column_names.append(att_name)
+ used_column_names.append(att_name)
- # Add primary_key and unique, if necessary.
- if column_name in indexes:
- if indexes[column_name]['primary_key']:
- extra_params['primary_key'] = True
- elif indexes[column_name]['unique']:
- extra_params['unique'] = True
+ # Add primary_key and unique, if necessary.
+ if column_name in indexes:
+ if indexes[column_name]['primary_key']:
+ extra_params['primary_key'] = True
+ elif indexes[column_name]['unique']:
+ extra_params['unique'] = True
- if is_relation:
- rel_to = "self" if relations[i][1] == table_name else table2model(relations[i][1])
- if rel_to in known_models:
- field_type = 'ForeignKey(%s' % rel_to
+ if is_relation:
+ rel_to = "self" if relations[i][1] == table_name else table2model(relations[i][1])
+ if rel_to in known_models:
+ field_type = 'ForeignKey(%s' % rel_to
+ else:
+ field_type = "ForeignKey('%s'" % rel_to
else:
- field_type = "ForeignKey('%s'" % rel_to
- else:
- # Calling `get_field_type` to get the field type string and any
- # additional paramters and notes.
- field_type, field_params, field_notes = self.get_field_type(connection, table_name, row)
- extra_params.update(field_params)
- comment_notes.extend(field_notes)
+ # Calling `get_field_type` to get the field type string and any
+ # additional paramters and notes.
+ field_type, field_params, field_notes = self.get_field_type(connection, table_name, row)
+ extra_params.update(field_params)
+ comment_notes.extend(field_notes)
- field_type += '('
+ field_type += '('
- # Don't output 'id = meta.AutoField(primary_key=True)', because
- # that's assumed if it doesn't exist.
- if att_name == 'id' and extra_params == {'primary_key': True}:
- if field_type == 'AutoField(':
- continue
- elif field_type == 'IntegerField(' and not connection.features.can_introspect_autofield:
- comment_notes.append('AutoField?')
+ # Don't output 'id = meta.AutoField(primary_key=True)', because
+ # that's assumed if it doesn't exist.
+ if att_name == 'id' and extra_params == {'primary_key': True}:
+ if field_type == 'AutoField(':
+ continue
+ elif field_type == 'IntegerField(' and not connection.features.can_introspect_autofield:
+ comment_notes.append('AutoField?')
- # Add 'null' and 'blank', if the 'null_ok' flag was present in the
- # table description.
- if row[6]: # If it's NULL...
- if field_type == 'BooleanField(':
- field_type = 'NullBooleanField('
- else:
- extra_params['blank'] = True
- if not field_type in ('TextField(', 'CharField('):
- extra_params['null'] = True
+ # Add 'null' and 'blank', if the 'null_ok' flag was present in the
+ # table description.
+ if row[6]: # If it's NULL...
+ if field_type == 'BooleanField(':
+ field_type = 'NullBooleanField('
+ else:
+ extra_params['blank'] = True
+ if not field_type in ('TextField(', 'CharField('):
+ extra_params['null'] = True
- field_desc = '%s = %s%s' % (
- att_name,
- # Custom fields will have a dotted path
- '' if '.' in field_type else 'models.',
- field_type,
- )
- if extra_params:
- if not field_desc.endswith('('):
- field_desc += ', '
- field_desc += ', '.join([
- '%s=%s' % (k, strip_prefix(repr(v)))
- for k, v in extra_params.items()])
- field_desc += ')'
- if comment_notes:
- field_desc += ' # ' + ' '.join(comment_notes)
- yield ' %s' % field_desc
- for meta_line in self.get_meta(table_name):
- yield meta_line
+ field_desc = '%s = %s%s' % (
+ att_name,
+ # Custom fields will have a dotted path
+ '' if '.' in field_type else 'models.',
+ field_type,
+ )
+ if extra_params:
+ if not field_desc.endswith('('):
+ field_desc += ', '
+ field_desc += ', '.join([
+ '%s=%s' % (k, strip_prefix(repr(v)))
+ for k, v in extra_params.items()])
+ field_desc += ')'
+ if comment_notes:
+ field_desc += ' # ' + ' '.join(comment_notes)
+ yield ' %s' % field_desc
+ for meta_line in self.get_meta(table_name):
+ yield meta_line
def normalize_col_name(self, col_name, used_column_names, is_relation):
"""
diff --git a/django/core/management/commands/loaddata.py b/django/core/management/commands/loaddata.py
index 65bc96ba99..31a3af6225 100644
--- a/django/core/management/commands/loaddata.py
+++ b/django/core/management/commands/loaddata.py
@@ -100,10 +100,9 @@ class Command(BaseCommand):
if sequence_sql:
if self.verbosity >= 2:
self.stdout.write("Resetting sequences\n")
- cursor = connection.cursor()
- for line in sequence_sql:
- cursor.execute(line)
- cursor.close()
+ with connection.cursor() as cursor:
+ for line in sequence_sql:
+ cursor.execute(line)
if self.verbosity >= 1:
if self.fixture_object_count == self.loaded_object_count:
diff --git a/django/core/management/commands/migrate.py b/django/core/management/commands/migrate.py
index cb863509c8..60899ef09f 100644
--- a/django/core/management/commands/migrate.py
+++ b/django/core/management/commands/migrate.py
@@ -171,105 +171,110 @@ class Command(BaseCommand):
"Runs the old syncdb-style operation on a list of app_labels."
cursor = connection.cursor()
- # Get a list of already installed *models* so that references work right.
- tables = connection.introspection.table_names()
- seen_models = connection.introspection.installed_models(tables)
- created_models = set()
- pending_references = {}
+ try:
+ # Get a list of already installed *models* so that references work right.
+ tables = connection.introspection.table_names(cursor)
+ seen_models = connection.introspection.installed_models(tables)
+ created_models = set()
+ pending_references = {}
- # Build the manifest of apps and models that are to be synchronized
- all_models = [
- (app_config.label,
- router.get_migratable_models(app_config, connection.alias, include_auto_created=True))
- for app_config in apps.get_app_configs()
- if app_config.models_module is not None and app_config.label in app_labels
- ]
+ # Build the manifest of apps and models that are to be synchronized
+ all_models = [
+ (app_config.label,
+ router.get_migratable_models(app_config, connection.alias, include_auto_created=True))
+ for app_config in apps.get_app_configs()
+ if app_config.models_module is not None and app_config.label in app_labels
+ ]
- def model_installed(model):
- opts = model._meta
- converter = connection.introspection.table_name_converter
- # Note that if a model is unmanaged we short-circuit and never try to install it
- return not ((converter(opts.db_table) in tables) or
- (opts.auto_created and converter(opts.auto_created._meta.db_table) in tables))
+ def model_installed(model):
+ opts = model._meta
+ converter = connection.introspection.table_name_converter
+ # Note that if a model is unmanaged we short-circuit and never try to install it
+ return not ((converter(opts.db_table) in tables) or
+ (opts.auto_created and converter(opts.auto_created._meta.db_table) in tables))
- manifest = OrderedDict(
- (app_name, list(filter(model_installed, model_list)))
- for app_name, model_list in all_models
- )
+ manifest = OrderedDict(
+ (app_name, list(filter(model_installed, model_list)))
+ for app_name, model_list in all_models
+ )
- create_models = set(itertools.chain(*manifest.values()))
- emit_pre_migrate_signal(create_models, self.verbosity, self.interactive, connection.alias)
+ create_models = set(itertools.chain(*manifest.values()))
+ emit_pre_migrate_signal(create_models, self.verbosity, self.interactive, connection.alias)
- # Create the tables for each model
- if self.verbosity >= 1:
- self.stdout.write(" Creating tables...\n")
- with transaction.atomic(using=connection.alias, savepoint=False):
- for app_name, model_list in manifest.items():
- for model in model_list:
- # Create the model's database table, if it doesn't already exist.
- if self.verbosity >= 3:
- self.stdout.write(" Processing %s.%s model\n" % (app_name, model._meta.object_name))
- sql, references = connection.creation.sql_create_model(model, no_style(), seen_models)
- seen_models.add(model)
- created_models.add(model)
- for refto, refs in references.items():
- pending_references.setdefault(refto, []).extend(refs)
- if refto in seen_models:
- sql.extend(connection.creation.sql_for_pending_references(refto, no_style(), pending_references))
- sql.extend(connection.creation.sql_for_pending_references(model, no_style(), pending_references))
- if self.verbosity >= 1 and sql:
- self.stdout.write(" Creating table %s\n" % model._meta.db_table)
- for statement in sql:
- cursor.execute(statement)
- tables.append(connection.introspection.table_name_converter(model._meta.db_table))
+ # Create the tables for each model
+ if self.verbosity >= 1:
+ self.stdout.write(" Creating tables...\n")
+ with transaction.atomic(using=connection.alias, savepoint=False):
+ for app_name, model_list in manifest.items():
+ for model in model_list:
+ # Create the model's database table, if it doesn't already exist.
+ if self.verbosity >= 3:
+ self.stdout.write(" Processing %s.%s model\n" % (app_name, model._meta.object_name))
+ sql, references = connection.creation.sql_create_model(model, no_style(), seen_models)
+ seen_models.add(model)
+ created_models.add(model)
+ for refto, refs in references.items():
+ pending_references.setdefault(refto, []).extend(refs)
+ if refto in seen_models:
+ sql.extend(connection.creation.sql_for_pending_references(refto, no_style(), pending_references))
+ sql.extend(connection.creation.sql_for_pending_references(model, no_style(), pending_references))
+ if self.verbosity >= 1 and sql:
+ self.stdout.write(" Creating table %s\n" % model._meta.db_table)
+ for statement in sql:
+ cursor.execute(statement)
+ tables.append(connection.introspection.table_name_converter(model._meta.db_table))
- # We force a commit here, as that was the previous behaviour.
- # If you can prove we don't need this, remove it.
- transaction.set_dirty(using=connection.alias)
+ # We force a commit here, as that was the previous behaviour.
+ # If you can prove we don't need this, remove it.
+ transaction.set_dirty(using=connection.alias)
+ finally:
+ cursor.close()
# The connection may have been closed by a syncdb handler.
cursor = connection.cursor()
+ try:
+ # Install custom SQL for the app (but only if this
+ # is a model we've just created)
+ if self.verbosity >= 1:
+ self.stdout.write(" Installing custom SQL...\n")
+ for app_name, model_list in manifest.items():
+ for model in model_list:
+ if model in created_models:
+ custom_sql = custom_sql_for_model(model, no_style(), connection)
+ if custom_sql:
+ if self.verbosity >= 2:
+ self.stdout.write(" Installing custom SQL for %s.%s model\n" % (app_name, model._meta.object_name))
+ try:
+ with transaction.commit_on_success_unless_managed(using=connection.alias):
+ for sql in custom_sql:
+ cursor.execute(sql)
+ except Exception as e:
+ self.stderr.write(" Failed to install custom SQL for %s.%s model: %s\n" % (app_name, model._meta.object_name, e))
+ if self.show_traceback:
+ traceback.print_exc()
+ else:
+ if self.verbosity >= 3:
+ self.stdout.write(" No custom SQL for %s.%s model\n" % (app_name, model._meta.object_name))
- # Install custom SQL for the app (but only if this
- # is a model we've just created)
- if self.verbosity >= 1:
- self.stdout.write(" Installing custom SQL...\n")
- for app_name, model_list in manifest.items():
- for model in model_list:
- if model in created_models:
- custom_sql = custom_sql_for_model(model, no_style(), connection)
- if custom_sql:
- if self.verbosity >= 2:
- self.stdout.write(" Installing custom SQL for %s.%s model\n" % (app_name, model._meta.object_name))
- try:
- with transaction.commit_on_success_unless_managed(using=connection.alias):
- for sql in custom_sql:
- cursor.execute(sql)
- except Exception as e:
- self.stderr.write(" Failed to install custom SQL for %s.%s model: %s\n" % (app_name, model._meta.object_name, e))
- if self.show_traceback:
- traceback.print_exc()
- else:
- if self.verbosity >= 3:
- self.stdout.write(" No custom SQL for %s.%s model\n" % (app_name, model._meta.object_name))
-
- if self.verbosity >= 1:
- self.stdout.write(" Installing indexes...\n")
+ if self.verbosity >= 1:
+ self.stdout.write(" Installing indexes...\n")
- # Install SQL indices for all newly created models
- for app_name, model_list in manifest.items():
- for model in model_list:
- if model in created_models:
- index_sql = connection.creation.sql_indexes_for_model(model, no_style())
- if index_sql:
- if self.verbosity >= 2:
- self.stdout.write(" Installing index for %s.%s model\n" % (app_name, model._meta.object_name))
- try:
- with transaction.commit_on_success_unless_managed(using=connection.alias):
- for sql in index_sql:
- cursor.execute(sql)
- except Exception as e:
- self.stderr.write(" Failed to install index for %s.%s model: %s\n" % (app_name, model._meta.object_name, e))
+ # Install SQL indices for all newly created models
+ for app_name, model_list in manifest.items():
+ for model in model_list:
+ if model in created_models:
+ index_sql = connection.creation.sql_indexes_for_model(model, no_style())
+ if index_sql:
+ if self.verbosity >= 2:
+ self.stdout.write(" Installing index for %s.%s model\n" % (app_name, model._meta.object_name))
+ try:
+ with transaction.commit_on_success_unless_managed(using=connection.alias):
+ for sql in index_sql:
+ cursor.execute(sql)
+ except Exception as e:
+ self.stderr.write(" Failed to install index for %s.%s model: %s\n" % (app_name, model._meta.object_name, e))
+ finally:
+ cursor.close()
# Load initial_data fixtures (unless that has been disabled)
if self.load_initial_data:
diff --git a/django/core/management/sql.py b/django/core/management/sql.py
index ad91ca36c6..ccab11d2bd 100644
--- a/django/core/management/sql.py
+++ b/django/core/management/sql.py
@@ -67,38 +67,39 @@ def sql_delete(app_config, style, connection):
except Exception:
cursor = None
- # Figure out which tables already exist
- if cursor:
- table_names = connection.introspection.table_names(cursor)
- else:
- table_names = []
-
- output = []
+ try:
+ # Figure out which tables already exist
+ if cursor:
+ table_names = connection.introspection.table_names(cursor)
+ else:
+ table_names = []
- # Output DROP TABLE statements for standard application tables.
- to_delete = set()
+ output = []
- references_to_delete = {}
- app_models = router.get_migratable_models(app_config, connection.alias, include_auto_created=True)
- for model in app_models:
- if cursor and connection.introspection.table_name_converter(model._meta.db_table) in table_names:
- # The table exists, so it needs to be dropped
- opts = model._meta
- for f in opts.local_fields:
- if f.rel and f.rel.to not in to_delete:
- references_to_delete.setdefault(f.rel.to, []).append((model, f))
+ # Output DROP TABLE statements for standard application tables.
+ to_delete = set()
- to_delete.add(model)
+ references_to_delete = {}
+ app_models = router.get_migratable_models(app_config, connection.alias, include_auto_created=True)
+ for model in app_models:
+ if cursor and connection.introspection.table_name_converter(model._meta.db_table) in table_names:
+ # The table exists, so it needs to be dropped
+ opts = model._meta
+ for f in opts.local_fields:
+ if f.rel and f.rel.to not in to_delete:
+ references_to_delete.setdefault(f.rel.to, []).append((model, f))
- for model in app_models:
- if connection.introspection.table_name_converter(model._meta.db_table) in table_names:
- output.extend(connection.creation.sql_destroy_model(model, references_to_delete, style))
+ to_delete.add(model)
- # Close database connection explicitly, in case this output is being piped
- # directly into a database client, to avoid locking issues.
- if cursor:
- cursor.close()
- connection.close()
+ for model in app_models:
+ if connection.introspection.table_name_converter(model._meta.db_table) in table_names:
+ output.extend(connection.creation.sql_destroy_model(model, references_to_delete, style))
+ finally:
+ # Close database connection explicitly, in case this output is being piped
+ # directly into a database client, to avoid locking issues.
+ if cursor:
+ cursor.close()
+ connection.close()
return output[::-1] # Reverse it, to deal with table dependencies.
diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py
index b96407056a..50b8745f47 100644
--- a/django/db/backends/__init__.py
+++ b/django/db/backends/__init__.py
@@ -194,13 +194,16 @@ class BaseDatabaseWrapper(object):
##### Backend-specific savepoint management methods #####
def _savepoint(self, sid):
- self.cursor().execute(self.ops.savepoint_create_sql(sid))
+ with self.cursor() as cursor:
+ cursor.execute(self.ops.savepoint_create_sql(sid))
def _savepoint_rollback(self, sid):
- self.cursor().execute(self.ops.savepoint_rollback_sql(sid))
+ with self.cursor() as cursor:
+ cursor.execute(self.ops.savepoint_rollback_sql(sid))
def _savepoint_commit(self, sid):
- self.cursor().execute(self.ops.savepoint_commit_sql(sid))
+ with self.cursor() as cursor:
+ cursor.execute(self.ops.savepoint_commit_sql(sid))
def _savepoint_allowed(self):
# Savepoints cannot be created outside a transaction
@@ -688,15 +691,15 @@ class BaseDatabaseFeatures(object):
# otherwise autocommit will cause the confimation to
# fail.
self.connection.enter_transaction_management()
- cursor = self.connection.cursor()
- cursor.execute('CREATE TABLE ROLLBACK_TEST (X INT)')
- self.connection.commit()
- cursor.execute('INSERT INTO ROLLBACK_TEST (X) VALUES (8)')
- self.connection.rollback()
- cursor.execute('SELECT COUNT(X) FROM ROLLBACK_TEST')
- count, = cursor.fetchone()
- cursor.execute('DROP TABLE ROLLBACK_TEST')
- self.connection.commit()
+ with self.connection.cursor() as cursor:
+ cursor.execute('CREATE TABLE ROLLBACK_TEST (X INT)')
+ self.connection.commit()
+ cursor.execute('INSERT INTO ROLLBACK_TEST (X) VALUES (8)')
+ self.connection.rollback()
+ cursor.execute('SELECT COUNT(X) FROM ROLLBACK_TEST')
+ count, = cursor.fetchone()
+ cursor.execute('DROP TABLE ROLLBACK_TEST')
+ self.connection.commit()
finally:
self.connection.leave_transaction_management()
return count == 0
@@ -1253,7 +1256,8 @@ class BaseDatabaseIntrospection(object):
in sorting order between databases.
"""
if cursor is None:
- cursor = self.connection.cursor()
+ with self.connection.cursor() as cursor:
+ return sorted(self.get_table_list(cursor))
return sorted(self.get_table_list(cursor))
def get_table_list(self, cursor):
diff --git a/django/db/backends/creation.py b/django/db/backends/creation.py
index ff62f30e71..3ee1e8448e 100644
--- a/django/db/backends/creation.py
+++ b/django/db/backends/creation.py
@@ -378,9 +378,8 @@ class BaseDatabaseCreation(object):
call_command('createcachetable', database=self.connection.alias)
- # Get a cursor (even though we don't need one yet). This has
- # the side effect of initializing the test database.
- self.connection.cursor()
+ # Ensure a connection for the side effect of initializing the test database.
+ self.connection.ensure_connection()
return test_database_name
@@ -406,34 +405,34 @@ class BaseDatabaseCreation(object):
qn = self.connection.ops.quote_name
# Create the test database and connect to it.
- cursor = self._nodb_connection.cursor()
- try:
- cursor.execute(
- "CREATE DATABASE %s %s" % (qn(test_database_name), suffix))
- except Exception as e:
- sys.stderr.write(
- "Got an error creating the test database: %s\n" % e)
- if not autoclobber:
- confirm = input(
- "Type 'yes' if you would like to try deleting the test "
- "database '%s', or 'no' to cancel: " % test_database_name)
- if autoclobber or confirm == 'yes':
- try:
- if verbosity >= 1:
- print("Destroying old test database '%s'..."
- % self.connection.alias)
- cursor.execute(
- "DROP DATABASE %s" % qn(test_database_name))
- cursor.execute(
- "CREATE DATABASE %s %s" % (qn(test_database_name),
- suffix))
- except Exception as e:
- sys.stderr.write(
- "Got an error recreating the test database: %s\n" % e)
- sys.exit(2)
- else:
- print("Tests cancelled.")
- sys.exit(1)
+ with self._nodb_connection.cursor() as cursor:
+ try:
+ cursor.execute(
+ "CREATE DATABASE %s %s" % (qn(test_database_name), suffix))
+ except Exception as e:
+ sys.stderr.write(
+ "Got an error creating the test database: %s\n" % e)
+ if not autoclobber:
+ confirm = input(
+ "Type 'yes' if you would like to try deleting the test "
+ "database '%s', or 'no' to cancel: " % test_database_name)
+ if autoclobber or confirm == 'yes':
+ try:
+ if verbosity >= 1:
+ print("Destroying old test database '%s'..."
+ % self.connection.alias)
+ cursor.execute(
+ "DROP DATABASE %s" % qn(test_database_name))
+ cursor.execute(
+ "CREATE DATABASE %s %s" % (qn(test_database_name),
+ suffix))
+ except Exception as e:
+ sys.stderr.write(
+ "Got an error recreating the test database: %s\n" % e)
+ sys.exit(2)
+ else:
+ print("Tests cancelled.")
+ sys.exit(1)
return test_database_name
@@ -461,11 +460,11 @@ class BaseDatabaseCreation(object):
# ourselves. Connect to the previous database (not the test database)
# to do so, because it's not allowed to delete a database while being
# connected to it.
- cursor = self._nodb_connection.cursor()
- # Wait to avoid "database is being accessed by other users" errors.
- time.sleep(1)
- cursor.execute("DROP DATABASE %s"
- % self.connection.ops.quote_name(test_database_name))
+ with self._nodb_connection.cursor() as cursor:
+ # Wait to avoid "database is being accessed by other users" errors.
+ time.sleep(1)
+ cursor.execute("DROP DATABASE %s"
+ % self.connection.ops.quote_name(test_database_name))
def set_autocommit(self):
"""
diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py
index e7932dd800..9d3935dc54 100644
--- a/django/db/backends/mysql/base.py
+++ b/django/db/backends/mysql/base.py
@@ -180,15 +180,15 @@ class DatabaseFeatures(BaseDatabaseFeatures):
@cached_property
def _mysql_storage_engine(self):
"Internal method used in Django tests. Don't rely on this from your code"
- cursor = self.connection.cursor()
- cursor.execute('CREATE TABLE INTROSPECT_TEST (X INT)')
- # This command is MySQL specific; the second column
- # will tell you the default table type of the created
- # table. Since all Django's test tables will have the same
- # table type, that's enough to evaluate the feature.
- cursor.execute("SHOW TABLE STATUS WHERE Name='INTROSPECT_TEST'")
- result = cursor.fetchone()
- cursor.execute('DROP TABLE INTROSPECT_TEST')
+ with self.connection.cursor() as cursor:
+ cursor.execute('CREATE TABLE INTROSPECT_TEST (X INT)')
+ # This command is MySQL specific; the second column
+ # will tell you the default table type of the created
+ # table. Since all Django's test tables will have the same
+ # table type, that's enough to evaluate the feature.
+ cursor.execute("SHOW TABLE STATUS WHERE Name='INTROSPECT_TEST'")
+ result = cursor.fetchone()
+ cursor.execute('DROP TABLE INTROSPECT_TEST')
return result[1]
@cached_property
@@ -207,9 +207,9 @@ class DatabaseFeatures(BaseDatabaseFeatures):
return False
# Test if the time zone definitions are installed.
- cursor = self.connection.cursor()
- cursor.execute("SELECT 1 FROM mysql.time_zone LIMIT 1")
- return cursor.fetchone() is not None
+ with self.connection.cursor() as cursor:
+ cursor.execute("SELECT 1 FROM mysql.time_zone LIMIT 1")
+ return cursor.fetchone() is not None
class DatabaseOperations(BaseDatabaseOperations):
@@ -461,13 +461,12 @@ class DatabaseWrapper(BaseDatabaseWrapper):
return conn
def init_connection_state(self):
- cursor = self.connection.cursor()
- # SQL_AUTO_IS_NULL in MySQL controls whether an AUTO_INCREMENT column
- # on a recently-inserted row will return when the field is tested for
- # NULL. Disabling this value brings this aspect of MySQL in line with
- # SQL standards.
- cursor.execute('SET SQL_AUTO_IS_NULL = 0')
- cursor.close()
+ with self.connection.cursor() as cursor:
+ # SQL_AUTO_IS_NULL in MySQL controls whether an AUTO_INCREMENT column
+ # on a recently-inserted row will return when the field is tested for
+ # NULL. Disabling this value brings this aspect of MySQL in line with
+ # SQL standards.
+ cursor.execute('SET SQL_AUTO_IS_NULL = 0')
def create_cursor(self):
cursor = self.connection.cursor()
diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py
index cdb101d20c..2495986a02 100644
--- a/django/db/backends/oracle/base.py
+++ b/django/db/backends/oracle/base.py
@@ -353,8 +353,8 @@ WHEN (new.%(col_name)s IS NULL)
def regex_lookup(self, lookup_type):
# If regex_lookup is called before it's been initialized, then create
# a cursor to initialize it and recur.
- self.connection.cursor()
- return self.connection.ops.regex_lookup(lookup_type)
+ with self.connection.cursor():
+ return self.connection.ops.regex_lookup(lookup_type)
def return_insert_id(self):
return "RETURNING %s INTO %%s", (InsertIdVar(),)
diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py
index 33f885d50c..e89a4e604a 100644
--- a/django/db/backends/postgresql_psycopg2/base.py
+++ b/django/db/backends/postgresql_psycopg2/base.py
@@ -149,8 +149,10 @@ class DatabaseWrapper(BaseDatabaseWrapper):
if conn_tz != tz:
cursor = self.connection.cursor()
- cursor.execute(self.ops.set_time_zone_sql(), [tz])
- cursor.close()
+ try:
+ cursor.execute(self.ops.set_time_zone_sql(), [tz])
+ finally:
+ cursor.close()
# Commit after setting the time zone (see #17062)
if not self.get_autocommit():
self.connection.commit()
diff --git a/django/db/backends/postgresql_psycopg2/version.py b/django/db/backends/postgresql_psycopg2/version.py
index dae94f2dac..64fd7c8298 100644
--- a/django/db/backends/postgresql_psycopg2/version.py
+++ b/django/db/backends/postgresql_psycopg2/version.py
@@ -39,6 +39,6 @@ def get_version(connection):
if hasattr(connection, 'server_version'):
return connection.server_version
else:
- cursor = connection.cursor()
- cursor.execute("SELECT version()")
- return _parse_version(cursor.fetchone()[0])
+ with connection.cursor() as cursor:
+ cursor.execute("SELECT version()")
+ return _parse_version(cursor.fetchone()[0])
diff --git a/django/db/backends/schema.py b/django/db/backends/schema.py
index e690567738..88cc894437 100644
--- a/django/db/backends/schema.py
+++ b/django/db/backends/schema.py
@@ -86,14 +86,13 @@ class BaseDatabaseSchemaEditor(object):
"""
Executes the given SQL statement, with optional parameters.
"""
- # Get the cursor
- cursor = self.connection.cursor()
# Log the command we're running, then run it
logger.debug("%s; (params %r)" % (sql, params))
if self.collect_sql:
self.collected_sql.append((sql % tuple(map(self.connection.ops.quote_parameter, params))) + ";")
else:
- cursor.execute(sql, params)
+ with self.connection.cursor() as cursor:
+ cursor.execute(sql, params)
def quote_name(self, name):
return self.connection.ops.quote_name(name)
@@ -791,7 +790,8 @@ class BaseDatabaseSchemaEditor(object):
Returns all constraint names matching the columns and conditions
"""
column_names = list(column_names) if column_names else None
- constraints = self.connection.introspection.get_constraints(self.connection.cursor(), model._meta.db_table)
+ with self.connection.cursor() as cursor:
+ constraints = self.connection.introspection.get_constraints(cursor, model._meta.db_table)
result = []
for name, infodict in constraints.items():
if column_names is None or column_names == infodict['columns']:
diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py
index 3c8f170b7d..2adfbacaa9 100644
--- a/django/db/backends/sqlite3/base.py
+++ b/django/db/backends/sqlite3/base.py
@@ -122,14 +122,14 @@ class DatabaseFeatures(BaseDatabaseFeatures):
rule out support for STDDEV. We need to manually check
whether the call works.
"""
- cursor = self.connection.cursor()
- cursor.execute('CREATE TABLE STDDEV_TEST (X INT)')
- try:
- cursor.execute('SELECT STDDEV(*) FROM STDDEV_TEST')
- has_support = True
- except utils.DatabaseError:
- has_support = False
- cursor.execute('DROP TABLE STDDEV_TEST')
+ with self.connection.cursor() as cursor:
+ cursor.execute('CREATE TABLE STDDEV_TEST (X INT)')
+ try:
+ cursor.execute('SELECT STDDEV(*) FROM STDDEV_TEST')
+ has_support = True
+ except utils.DatabaseError:
+ has_support = False
+ cursor.execute('DROP TABLE STDDEV_TEST')
return has_support
@cached_property
diff --git a/django/db/models/query.py b/django/db/models/query.py
index 353dd95794..6051b9f859 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -1522,54 +1522,59 @@ class RawQuerySet(object):
query = iter(self.query)
- # Find out which columns are model's fields, and which ones should be
- # annotated to the model.
- for pos, column in enumerate(self.columns):
- if column in self.model_fields:
- model_init_field_names[self.model_fields[column].attname] = pos
- else:
- annotation_fields.append((column, pos))
+ try:
+ # Find out which columns are model's fields, and which ones should be
+ # annotated to the model.
+ for pos, column in enumerate(self.columns):
+ if column in self.model_fields:
+ model_init_field_names[self.model_fields[column].attname] = pos
+ else:
+ annotation_fields.append((column, pos))
- # Find out which model's fields are not present in the query.
- skip = set()
- for field in self.model._meta.fields:
- if field.attname not in model_init_field_names:
- skip.add(field.attname)
- if skip:
- if self.model._meta.pk.attname in skip:
- raise InvalidQuery('Raw query must include the primary key')
- model_cls = deferred_class_factory(self.model, skip)
- else:
- model_cls = self.model
- # All model's fields are present in the query. So, it is possible
- # to use *args based model instantation. For each field of the model,
- # record the query column position matching that field.
- model_init_field_pos = []
+ # Find out which model's fields are not present in the query.
+ skip = set()
for field in self.model._meta.fields:
- model_init_field_pos.append(model_init_field_names[field.attname])
- if need_resolv_columns:
- fields = [self.model_fields.get(c, None) for c in self.columns]
- # Begin looping through the query values.
- for values in query:
- if need_resolv_columns:
- values = compiler.resolve_columns(values, fields)
- # Associate fields to values
+ if field.attname not in model_init_field_names:
+ skip.add(field.attname)
if skip:
- model_init_kwargs = {}
- for attname, pos in six.iteritems(model_init_field_names):
- model_init_kwargs[attname] = values[pos]
- instance = model_cls(**model_init_kwargs)
+ if self.model._meta.pk.attname in skip:
+ raise InvalidQuery('Raw query must include the primary key')
+ model_cls = deferred_class_factory(self.model, skip)
else:
- model_init_args = [values[pos] for pos in model_init_field_pos]
- instance = model_cls(*model_init_args)
- if annotation_fields:
- for column, pos in annotation_fields:
- setattr(instance, column, values[pos])
+ model_cls = self.model
+ # All model's fields are present in the query. So, it is possible
+ # to use *args based model instantation. For each field of the model,
+ # record the query column position matching that field.
+ model_init_field_pos = []
+ for field in self.model._meta.fields:
+ model_init_field_pos.append(model_init_field_names[field.attname])
+ if need_resolv_columns:
+ fields = [self.model_fields.get(c, None) for c in self.columns]
+ # Begin looping through the query values.
+ for values in query:
+ if need_resolv_columns:
+ values = compiler.resolve_columns(values, fields)
+ # Associate fields to values
+ if skip:
+ model_init_kwargs = {}
+ for attname, pos in six.iteritems(model_init_field_names):
+ model_init_kwargs[attname] = values[pos]
+ instance = model_cls(**model_init_kwargs)
+ else:
+ model_init_args = [values[pos] for pos in model_init_field_pos]
+ instance = model_cls(*model_init_args)
+ if annotation_fields:
+ for column, pos in annotation_fields:
+ setattr(instance, column, values[pos])
- instance._state.db = db
- instance._state.adding = False
+ instance._state.db = db
+ instance._state.adding = False
- yield instance
+ yield instance
+ finally:
+ # Done iterating the Query. If it has its own cursor, close it.
+ if hasattr(self.query, 'cursor') and self.query.cursor:
+ self.query.cursor.close()
def __repr__(self):
text = self.raw_query
diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py
index 536a66d139..d9161d820c 100644
--- a/django/db/models/sql/compiler.py
+++ b/django/db/models/sql/compiler.py
@@ -1,4 +1,5 @@
import datetime
+import sys
from django.conf import settings
from django.core.exceptions import FieldError
@@ -777,7 +778,7 @@ class SQLCompiler(object):
cursor = self.connection.cursor()
try:
cursor.execute(sql, params)
- except:
+ except Exception:
cursor.close()
raise
@@ -908,15 +909,15 @@ class SQLInsertCompiler(SQLCompiler):
def execute_sql(self, return_id=False):
assert not (return_id and len(self.query.objs) != 1)
self.return_id = return_id
- cursor = self.connection.cursor()
- for sql, params in self.as_sql():
- cursor.execute(sql, params)
- if not (return_id and cursor):
- return
- if self.connection.features.can_return_id_from_insert:
- return self.connection.ops.fetch_returned_insert_id(cursor)
- return self.connection.ops.last_insert_id(cursor,
- self.query.get_meta().db_table, self.query.get_meta().pk.column)
+ with self.connection.cursor() as cursor:
+ for sql, params in self.as_sql():
+ cursor.execute(sql, params)
+ if not (return_id and cursor):
+ return
+ if self.connection.features.can_return_id_from_insert:
+ return self.connection.ops.fetch_returned_insert_id(cursor)
+ return self.connection.ops.last_insert_id(cursor,
+ self.query.get_meta().db_table, self.query.get_meta().pk.column)
class SQLDeleteCompiler(SQLCompiler):