Fixed #32374 -- Stopped recording migration application before deferred SQL.

Migrations cannot be recorded in the same transaction as its associated
DDL operations when some of it is deferred until the schema editor
context exits.

Regression in c86a3d80a2.
This commit is contained in:
Simon Charette 2021-01-20 19:00:36 -05:00 committed by Mariusz Felisiak
parent 533a583578
commit 0c42cdf0d2
2 changed files with 30 additions and 2 deletions

View File

@ -225,8 +225,9 @@ class MigrationExecutor:
# Alright, do it normally
with self.connection.schema_editor(atomic=migration.atomic) as schema_editor:
state = migration.apply(state, schema_editor)
self.record_migration(migration)
migration_recorded = True
if not schema_editor.deferred_sql:
self.record_migration(migration)
migration_recorded = True
if not migration_recorded:
self.record_migration(migration)
# Report progress

View File

@ -684,6 +684,33 @@ class ExecutorTests(MigrationTestBase):
)
self.assertTableNotExists('record_migration_model')
def test_migrations_not_applied_on_deferred_sql_failure(self):
"""Migrations are not recorded if deferred SQL application fails."""
class DeferredSQL:
def __str__(self):
raise DatabaseError('Failed to apply deferred SQL')
class Migration(migrations.Migration):
atomic = False
def apply(self, project_state, schema_editor, collect_sql=False):
schema_editor.deferred_sql.append(DeferredSQL())
executor = MigrationExecutor(connection)
with self.assertRaisesMessage(DatabaseError, 'Failed to apply deferred SQL'):
executor.apply_migration(
ProjectState(),
Migration('0001_initial', 'deferred_sql'),
)
# The migration isn't recorded as applied since it failed.
migration_recorder = MigrationRecorder(connection)
self.assertIs(
migration_recorder.migration_qs.filter(
app='deferred_sql', name='0001_initial',
).exists(),
False,
)
class FakeLoader:
def __init__(self, graph, applied):