summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
Diffstat (limited to 'django')
-rw-r--r--django/core/management/commands/flush.py4
-rw-r--r--django/core/management/sql.py7
-rw-r--r--django/db/backends/__init__.py13
-rw-r--r--django/db/backends/mysql/base.py27
-rw-r--r--django/db/backends/oracle/base.py21
-rw-r--r--django/db/backends/postgresql_psycopg2/operations.py34
-rw-r--r--django/test/simple.py4
-rw-r--r--django/test/testcases.py62
8 files changed, 111 insertions, 61 deletions
diff --git a/django/core/management/commands/flush.py b/django/core/management/commands/flush.py
index 2fc2e7ed26..ac7b7a3599 100644
--- a/django/core/management/commands/flush.py
+++ b/django/core/management/commands/flush.py
@@ -29,6 +29,8 @@ class Command(NoArgsCommand):
connection = connections[db]
verbosity = int(options.get('verbosity'))
interactive = options.get('interactive')
+ # 'reset_sequences' is a stealth option
+ reset_sequences = options.get('reset_sequences', True)
self.style = no_style()
@@ -40,7 +42,7 @@ class Command(NoArgsCommand):
except ImportError:
pass
- sql_list = sql_flush(self.style, connection, only_django=True)
+ sql_list = sql_flush(self.style, connection, only_django=True, reset_sequences=reset_sequences)
if interactive:
confirm = raw_input("""You have requested a flush of the database.
diff --git a/django/core/management/sql.py b/django/core/management/sql.py
index 46d3cf28ed..7579cbe8ab 100644
--- a/django/core/management/sql.py
+++ b/django/core/management/sql.py
@@ -98,7 +98,7 @@ def sql_delete(app, style, connection):
return output[::-1] # Reverse it, to deal with table dependencies.
-def sql_flush(style, connection, only_django=False):
+def sql_flush(style, connection, only_django=False, reset_sequences=True):
"""
Returns a list of the SQL statements used to flush the database.
@@ -109,9 +109,8 @@ def sql_flush(style, connection, only_django=False):
tables = connection.introspection.django_table_names(only_existing=True)
else:
tables = connection.introspection.table_names()
- statements = connection.ops.sql_flush(
- style, tables, connection.introspection.sequence_list()
- )
+ seqs = connection.introspection.sequence_list() if reset_sequences else ()
+ statements = connection.ops.sql_flush(style, tables, seqs)
return statements
def sql_custom(app, style, connection):
diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py
index a896f5fd08..6e23ad5bb5 100644
--- a/django/db/backends/__init__.py
+++ b/django/db/backends/__init__.py
@@ -748,11 +748,24 @@ class BaseDatabaseOperations(object):
the given database tables (without actually removing the tables
themselves).
+ The returned value also includes SQL statements required to reset DB
+ sequences passed in :param sequences:.
+
The `style` argument is a Style object as returned by either
color_style() or no_style() in django.core.management.color.
"""
raise NotImplementedError()
+ def sequence_reset_by_name_sql(self, style, sequences):
+ """
+ Returns a list of the SQL statements required to reset sequences
+ passed in :param sequences:.
+
+ The `style` argument is a Style object as returned by either
+ color_style() or no_style() in django.core.management.color.
+ """
+ return []
+
def sequence_reset_sql(self, style, model_list):
"""
Returns a list of the SQL statements required to reset sequences for
diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py
index ec65207ed8..2222f89cf0 100644
--- a/django/db/backends/mysql/base.py
+++ b/django/db/backends/mysql/base.py
@@ -262,22 +262,25 @@ class DatabaseOperations(BaseDatabaseOperations):
for table in tables:
sql.append('%s %s;' % (style.SQL_KEYWORD('TRUNCATE'), style.SQL_FIELD(self.quote_name(table))))
sql.append('SET FOREIGN_KEY_CHECKS = 1;')
-
- # Truncate already resets the AUTO_INCREMENT field from
- # MySQL version 5.0.13 onwards. Refs #16961.
- if self.connection.mysql_version < (5,0,13):
- sql.extend(
- ["%s %s %s %s %s;" % \
- (style.SQL_KEYWORD('ALTER'),
- style.SQL_KEYWORD('TABLE'),
- style.SQL_TABLE(self.quote_name(sequence['table'])),
- style.SQL_KEYWORD('AUTO_INCREMENT'),
- style.SQL_FIELD('= 1'),
- ) for sequence in sequences])
+ sql.extend(self.sequence_reset_by_name_sql(style, sequences))
return sql
else:
return []
+ 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;" % \
+ (style.SQL_KEYWORD('ALTER'),
+ style.SQL_KEYWORD('TABLE'),
+ style.SQL_TABLE(self.quote_name(sequence['table'])),
+ style.SQL_KEYWORD('AUTO_INCREMENT'),
+ style.SQL_FIELD('= 1'),
+ ) for sequence in sequences]
+ else:
+ return []
+
def validate_autopk_value(self, value):
# MySQLism: zero in AUTO_INCREMENT field does not work. Refs #17653.
if value == 0:
diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py
index 32ae420ce0..b08113fed7 100644
--- a/django/db/backends/oracle/base.py
+++ b/django/db/backends/oracle/base.py
@@ -298,18 +298,23 @@ WHEN (new.%(col_name)s IS NULL)
for table in tables]
# Since we've just deleted all the rows, running our sequence
# ALTER code will reset the sequence to 0.
- for sequence_info in sequences:
- sequence_name = self._get_sequence_name(sequence_info['table'])
- table_name = self.quote_name(sequence_info['table'])
- column_name = self.quote_name(sequence_info['column'] or 'id')
- query = _get_sequence_reset_sql() % {'sequence': sequence_name,
- 'table': table_name,
- 'column': column_name}
- sql.append(query)
+ sql.extend(self.sequence_reset_by_name_sql(style, sequences))
return sql
else:
return []
+ def sequence_reset_by_name_sql(self, style, sequences):
+ sql = []
+ for sequence_info in sequences:
+ sequence_name = self._get_sequence_name(sequence_info['table'])
+ table_name = self.quote_name(sequence_info['table'])
+ column_name = self.quote_name(sequence_info['column'] or 'id')
+ query = _get_sequence_reset_sql() % {'sequence': sequence_name,
+ 'table': table_name,
+ 'column': column_name}
+ sql.append(query)
+ return sql
+
def sequence_reset_sql(self, style, model_list):
from django.db import models
output = []
diff --git a/django/db/backends/postgresql_psycopg2/operations.py b/django/db/backends/postgresql_psycopg2/operations.py
index e93a15512b..40fe629110 100644
--- a/django/db/backends/postgresql_psycopg2/operations.py
+++ b/django/db/backends/postgresql_psycopg2/operations.py
@@ -85,25 +85,29 @@ class DatabaseOperations(BaseDatabaseOperations):
(style.SQL_KEYWORD('TRUNCATE'),
style.SQL_FIELD(', '.join([self.quote_name(table) for table in tables]))
)]
-
- # 'ALTER SEQUENCE sequence_name RESTART WITH 1;'... style SQL statements
- # to reset sequence indices
- for sequence_info in sequences:
- table_name = sequence_info['table']
- column_name = sequence_info['column']
- if not (column_name and len(column_name) > 0):
- # 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);" % \
- (style.SQL_KEYWORD('SELECT'),
- style.SQL_TABLE(self.quote_name(table_name)),
- style.SQL_FIELD(column_name))
- )
+ sql.extend(self.sequence_reset_by_name_sql(style, sequences))
return sql
else:
return []
+ def sequence_reset_by_name_sql(self, style, sequences):
+ # 'ALTER SEQUENCE sequence_name RESTART WITH 1;'... style SQL statements
+ # to reset sequence indices
+ sql = []
+ for sequence_info in sequences:
+ table_name = sequence_info['table']
+ column_name = sequence_info['column']
+ if not (column_name and len(column_name) > 0):
+ # 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);" % \
+ (style.SQL_KEYWORD('SELECT'),
+ style.SQL_TABLE(self.quote_name(table_name)),
+ style.SQL_FIELD(column_name))
+ )
+ return sql
+
def tablespace_sql(self, tablespace, inline=False):
if inline:
return "USING INDEX TABLESPACE %s" % self.quote_name(tablespace)
diff --git a/django/test/simple.py b/django/test/simple.py
index 4f05284543..bf0219d53f 100644
--- a/django/test/simple.py
+++ b/django/test/simple.py
@@ -5,7 +5,7 @@ from django.core.exceptions import ImproperlyConfigured
from django.db.models import get_app, get_apps
from django.test import _doctest as doctest
from django.test.utils import setup_test_environment, teardown_test_environment
-from django.test.testcases import OutputChecker, DocTestRunner, TestCase
+from django.test.testcases import OutputChecker, DocTestRunner
from django.utils import unittest
from django.utils.importlib import import_module
from django.utils.module_loading import module_has_submodule
@@ -263,7 +263,7 @@ class DjangoTestSuiteRunner(object):
for test in extra_tests:
suite.addTest(test)
- return reorder_suite(suite, (TestCase,))
+ return reorder_suite(suite, (unittest.TestCase,))
def setup_databases(self, **kwargs):
from django.db import connections, DEFAULT_DB_ALIAS
diff --git a/django/test/testcases.py b/django/test/testcases.py
index b9aae21e8e..b60188bf30 100644
--- a/django/test/testcases.py
+++ b/django/test/testcases.py
@@ -23,6 +23,7 @@ from django.core import mail
from django.core.exceptions import ValidationError, ImproperlyConfigured
from django.core.handlers.wsgi import WSGIHandler
from django.core.management import call_command
+from django.core.management.color import no_style
from django.core.signals import request_started
from django.core.servers.basehttp import (WSGIRequestHandler, WSGIServer,
WSGIServerException)
@@ -444,10 +445,15 @@ class SimpleTestCase(ut2.TestCase):
class TransactionTestCase(SimpleTestCase):
+
# The class we'll use for the test client self.client.
# Can be overridden in derived classes.
client_class = Client
+ # Subclasses can ask for resetting of auto increment sequence before each
+ # test case
+ reset_sequences = False
+
def _pre_setup(self):
"""Performs any pre-test setup. This includes:
@@ -462,22 +468,36 @@ class TransactionTestCase(SimpleTestCase):
self._urlconf_setup()
mail.outbox = []
+ def _reset_sequences(self, db_name):
+ conn = connections[db_name]
+ if conn.features.supports_sequence_reset:
+ sql_list = \
+ conn.ops.sequence_reset_by_name_sql(no_style(),
+ conn.introspection.sequence_list())
+ if sql_list:
+ try:
+ cursor = conn.cursor()
+ for sql in sql_list:
+ cursor.execute(sql)
+ except Exception:
+ transaction.rollback_unless_managed(using=db_name)
+ raise
+ transaction.commit_unless_managed(using=db_name)
+
def _fixture_setup(self):
- # If the test case has a multi_db=True flag, flush all databases.
- # Otherwise, just flush default.
- if getattr(self, 'multi_db', False):
- databases = connections
- else:
- databases = [DEFAULT_DB_ALIAS]
- for db in databases:
- call_command('flush', verbosity=0, interactive=False, database=db,
- skip_validation=True)
+ # If the test case has a multi_db=True flag, act on all databases.
+ # Otherwise, just on the default DB.
+ db_names = connections if getattr(self, 'multi_db', False) else [DEFAULT_DB_ALIAS]
+ for db_name in db_names:
+ # Reset sequences
+ if self.reset_sequences:
+ self._reset_sequences(db_name)
if hasattr(self, 'fixtures'):
# We have to use this slightly awkward syntax due to the fact
# that we're using *args and **kwargs together.
call_command('loaddata', *self.fixtures,
- **{'verbosity': 0, 'database': db, 'skip_validation': True})
+ **{'verbosity': 0, 'database': db_name, 'skip_validation': True})
def _urlconf_setup(self):
if hasattr(self, 'urls'):
@@ -534,7 +554,12 @@ class TransactionTestCase(SimpleTestCase):
conn.close()
def _fixture_teardown(self):
- pass
+ # If the test case has a multi_db=True flag, flush all databases.
+ # Otherwise, just flush default.
+ databases = connections if getattr(self, 'multi_db', False) else [DEFAULT_DB_ALIAS]
+ for db in databases:
+ call_command('flush', verbosity=0, interactive=False, database=db,
+ skip_validation=True, reset_sequences=False)
def _urlconf_teardown(self):
if hasattr(self, '_old_root_urlconf'):
@@ -808,22 +833,21 @@ class TestCase(TransactionTestCase):
if not connections_support_transactions():
return super(TestCase, self)._fixture_setup()
+ assert not self.reset_sequences, 'reset_sequences cannot be used on TestCase instances'
+
# If the test case has a multi_db=True flag, setup all databases.
# Otherwise, just use default.
- if getattr(self, 'multi_db', False):
- databases = connections
- else:
- databases = [DEFAULT_DB_ALIAS]
+ db_names = connections if getattr(self, 'multi_db', False) else [DEFAULT_DB_ALIAS]
- for db in databases:
- transaction.enter_transaction_management(using=db)
- transaction.managed(True, using=db)
+ for db_name in db_names:
+ transaction.enter_transaction_management(using=db_name)
+ transaction.managed(True, using=db_name)
disable_transaction_methods()
from django.contrib.sites.models import Site
Site.objects.clear_cache()
- for db in databases:
+ for db in db_names:
if hasattr(self, 'fixtures'):
call_command('loaddata', *self.fixtures,
**{