Fixed #31318 -- Allowed sqlmigrate to inspect squashed migrations.

This commit is contained in:
David Wobrock 2020-03-08 22:48:59 +01:00 committed by Mariusz Felisiak
parent 271e108b29
commit d88365708c
3 changed files with 37 additions and 21 deletions

View File

@ -32,8 +32,9 @@ class Command(BaseCommand):
# Get the database we're operating from # Get the database we're operating from
connection = connections[options['database']] connection = connections[options['database']]
# Load up an loader to get all the migration data # Load up an loader to get all the migration data, but don't replace
loader = MigrationLoader(connection) # migrations.
loader = MigrationLoader(connection, replace_migrations=False)
# Resolve command-line arguments into a migration # Resolve command-line arguments into a migration
app_label, migration_name = options['app_label'], options['migration_name'] app_label, migration_name = options['app_label'], options['migration_name']

View File

@ -40,11 +40,15 @@ class MigrationLoader:
in memory. in memory.
""" """
def __init__(self, connection, load=True, ignore_no_migrations=False): def __init__(
self, connection, load=True, ignore_no_migrations=False,
replace_migrations=True,
):
self.connection = connection self.connection = connection
self.disk_migrations = None self.disk_migrations = None
self.applied_migrations = None self.applied_migrations = None
self.ignore_no_migrations = ignore_no_migrations self.ignore_no_migrations = ignore_no_migrations
self.replace_migrations = replace_migrations
if load: if load:
self.build_graph() self.build_graph()
@ -223,24 +227,27 @@ class MigrationLoader:
# Add external dependencies now that the internal ones have been resolved. # Add external dependencies now that the internal ones have been resolved.
for key, migration in self.disk_migrations.items(): for key, migration in self.disk_migrations.items():
self.add_external_dependencies(key, migration) self.add_external_dependencies(key, migration)
# Carry out replacements where possible. # Carry out replacements where possible and if enabled.
for key, migration in self.replacements.items(): if self.replace_migrations:
# Get applied status of each of this migration's replacement targets. for key, migration in self.replacements.items():
applied_statuses = [(target in self.applied_migrations) for target in migration.replaces] # Get applied status of each of this migration's replacement
# Ensure the replacing migration is only marked as applied if all of # targets.
# its replacement targets are. applied_statuses = [(target in self.applied_migrations) for target in migration.replaces]
if all(applied_statuses): # The replacing migration is only marked as applied if all of
self.applied_migrations[key] = migration # its replacement targets are.
else: if all(applied_statuses):
self.applied_migrations.pop(key, None) self.applied_migrations[key] = migration
# A replacing migration can be used if either all or none of its else:
# replacement targets have been applied. self.applied_migrations.pop(key, None)
if all(applied_statuses) or (not any(applied_statuses)): # A replacing migration can be used if either all or none of
self.graph.remove_replaced_nodes(key, migration.replaces) # its replacement targets have been applied.
else: if all(applied_statuses) or (not any(applied_statuses)):
# This replacing migration cannot be used because it is partially applied. self.graph.remove_replaced_nodes(key, migration.replaces)
# Remove it from the graph and remap dependencies to it (#25945). else:
self.graph.remove_replacement_node(key, migration.replaces) # This replacing migration cannot be used because it is
# partially applied. Remove it from the graph and remap
# dependencies to it (#25945).
self.graph.remove_replacement_node(key, migration.replaces)
# Ensure the graph is consistent. # Ensure the graph is consistent.
try: try:
self.graph.validate_consistency() self.graph.validate_consistency()

View File

@ -713,6 +713,14 @@ class MigrateTests(MigrationTestBase):
self.assertIn('-- create model book', output) self.assertIn('-- create model book', output)
self.assertNotIn('-- create model tribble', output) self.assertNotIn('-- create model tribble', output)
@override_settings(MIGRATION_MODULES={'migrations': 'migrations.test_migrations_squashed'})
def test_sqlmigrate_replaced_migration(self):
out = io.StringIO()
call_command('sqlmigrate', 'migrations', '0001_initial', stdout=out)
output = out.getvalue().lower()
self.assertIn('-- create model author', output)
self.assertIn('-- create model tribble', output)
@override_settings(MIGRATION_MODULES={'migrations': 'migrations.test_migrations_no_operations'}) @override_settings(MIGRATION_MODULES={'migrations': 'migrations.test_migrations_no_operations'})
def test_migrations_no_operations(self): def test_migrations_no_operations(self):
err = io.StringIO() err = io.StringIO()