mirror of
https://github.com/django/django.git
synced 2025-10-26 07:06:08 +00:00
Fixed #25833 -- Added support for non-atomic migrations.
Added the Migration.atomic attribute which can be set to False for non-atomic migrations.
This commit is contained in:
@@ -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'])]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user