summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorPankrat <lhaehne@gmail.com>2016-01-30 21:46:28 +0100
committerTim Graham <timograham@gmail.com>2016-02-05 09:09:05 -0500
commitf91a04621ea8d7a657ff755645d823026257e898 (patch)
tree2b06f3ce1cbb8e5f04499838154cf715ff76ce5b /django
parent0edb8a146fd6c60f4c3b1ad0a4a89963962f22af (diff)
Fixed #25833 -- Added support for non-atomic migrations.
Added the Migration.atomic attribute which can be set to False for non-atomic migrations.
Diffstat (limited to 'django')
-rw-r--r--django/core/management/commands/sqlmigrate.py3
-rw-r--r--django/db/backends/base/schema.py7
-rw-r--r--django/db/migrations/executor.py6
-rw-r--r--django/db/migrations/migration.py10
-rw-r--r--django/db/migrations/operations/special.py4
5 files changed, 20 insertions, 10 deletions
diff --git a/django/core/management/commands/sqlmigrate.py b/django/core/management/commands/sqlmigrate.py
index 14c2742c07..b858b2b805 100644
--- a/django/core/management/commands/sqlmigrate.py
+++ b/django/core/management/commands/sqlmigrate.py
@@ -51,6 +51,9 @@ class Command(BaseCommand):
migration_name, app_label))
targets = [(app_label, migration.name)]
+ # Show begin/end around output only for atomic migrations
+ self.output_transaction = migration.atomic
+
# Make a plan that represents just the requested migrations and show SQL
# for it
plan = [(executor.loader.graph.nodes[targets[0]], options['backwards'])]
diff --git a/django/db/backends/base/schema.py b/django/db/backends/base/schema.py
index 35dcb2e296..a9d84e114d 100644
--- a/django/db/backends/base/schema.py
+++ b/django/db/backends/base/schema.py
@@ -69,17 +69,18 @@ class BaseDatabaseSchemaEditor(object):
sql_create_pk = "ALTER TABLE %(table)s ADD CONSTRAINT %(name)s PRIMARY KEY (%(columns)s)"
sql_delete_pk = "ALTER TABLE %(table)s DROP CONSTRAINT %(name)s"
- def __init__(self, connection, collect_sql=False):
+ def __init__(self, connection, collect_sql=False, atomic=True):
self.connection = connection
self.collect_sql = collect_sql
if self.collect_sql:
self.collected_sql = []
+ self.atomic_migration = self.connection.features.can_rollback_ddl and atomic
# State-managing methods
def __enter__(self):
self.deferred_sql = []
- if self.connection.features.can_rollback_ddl:
+ if self.atomic_migration:
self.atomic = atomic(self.connection.alias)
self.atomic.__enter__()
return self
@@ -88,7 +89,7 @@ class BaseDatabaseSchemaEditor(object):
if exc_type is None:
for sql in self.deferred_sql:
self.execute(sql)
- if self.connection.features.can_rollback_ddl:
+ if self.atomic_migration:
self.atomic.__exit__(exc_type, exc_value, traceback)
# Core utility functions
diff --git a/django/db/migrations/executor.py b/django/db/migrations/executor.py
index b1a8383100..805b6cac87 100644
--- a/django/db/migrations/executor.py
+++ b/django/db/migrations/executor.py
@@ -170,7 +170,7 @@ class MigrationExecutor(object):
statements = []
state = None
for migration, backwards in plan:
- with self.connection.schema_editor(collect_sql=True) as schema_editor:
+ with self.connection.schema_editor(collect_sql=True, atomic=migration.atomic) as schema_editor:
if state is None:
state = self.loader.project_state((migration.app_label, migration.name), at_end=False)
if not backwards:
@@ -194,7 +194,7 @@ class MigrationExecutor(object):
fake = True
if not fake:
# Alright, do it normally
- with self.connection.schema_editor() as schema_editor:
+ with self.connection.schema_editor(atomic=migration.atomic) as schema_editor:
state = migration.apply(state, schema_editor)
# For replacement migrations, record individual statuses
if migration.replaces:
@@ -214,7 +214,7 @@ class MigrationExecutor(object):
if self.progress_callback:
self.progress_callback("unapply_start", migration, fake)
if not fake:
- with self.connection.schema_editor() as schema_editor:
+ with self.connection.schema_editor(atomic=migration.atomic) as schema_editor:
state = migration.unapply(state, schema_editor)
# For replacement migrations, record individual statuses
if migration.replaces:
diff --git a/django/db/migrations/migration.py b/django/db/migrations/migration.py
index 7641eb45bc..732dcb82e8 100644
--- a/django/db/migrations/migration.py
+++ b/django/db/migrations/migration.py
@@ -48,6 +48,10 @@ class Migration(object):
# introspection. If False, never perform introspection.
initial = None
+ # Whether to wrap the whole migration in a transaction. Only has an effect
+ # on database backends which support transactional DDL.
+ atomic = True
+
def __init__(self, name, app_label):
self.name = name
self.app_label = app_label
@@ -114,8 +118,10 @@ class Migration(object):
old_state = project_state.clone()
operation.state_forwards(self.app_label, project_state)
# Run the operation
- if not schema_editor.connection.features.can_rollback_ddl and operation.atomic:
- # We're forcing a transaction on a non-transactional-DDL backend
+ atomic_operation = operation.atomic or (self.atomic and operation.atomic is not False)
+ if not schema_editor.atomic_migration and atomic_operation:
+ # Force a transaction on a non-transactional-DDL backend or an
+ # atomic operation inside a non-atomic migration.
with atomic(schema_editor.connection.alias):
operation.database_forwards(self.app_label, schema_editor, old_state, project_state)
else:
diff --git a/django/db/migrations/operations/special.py b/django/db/migrations/operations/special.py
index a7da3cc81c..cd1e2479db 100644
--- a/django/db/migrations/operations/special.py
+++ b/django/db/migrations/operations/special.py
@@ -139,7 +139,7 @@ class RunPython(Operation):
reduces_to_sql = False
- def __init__(self, code, reverse_code=None, atomic=True, hints=None, elidable=False):
+ def __init__(self, code, reverse_code=None, atomic=None, hints=None, elidable=False):
self.atomic = atomic
# Forwards code
if not callable(code):
@@ -161,7 +161,7 @@ class RunPython(Operation):
}
if self.reverse_code is not None:
kwargs['reverse_code'] = self.reverse_code
- if self.atomic is not True:
+ if self.atomic is not None:
kwargs['atomic'] = self.atomic
if self.hints:
kwargs['hints'] = self.hints