Refs #31703 -- Moved MigrationAutodetector.suggest_name() to Migration.

Allows expanding the method to inspect additional attributes of the
migration instance. For example, the Migration.initial attribute.
This commit is contained in:
Jon Dufresne 2020-06-13 10:06:05 -07:00 committed by Mariusz Felisiak
parent 798835112d
commit bce180dd05
3 changed files with 55 additions and 38 deletions

View File

@ -9,9 +9,7 @@ from django.db.migrations.migration import Migration
from django.db.migrations.operations.models import AlterModelOptions
from django.db.migrations.optimizer import MigrationOptimizer
from django.db.migrations.questioner import MigrationQuestioner
from django.db.migrations.utils import (
COMPILED_REGEX_TYPE, RegexObject, get_migration_name_timestamp,
)
from django.db.migrations.utils import COMPILED_REGEX_TYPE, RegexObject
from django.utils.topological_sort import stable_topological_sort
@ -1265,13 +1263,14 @@ class MigrationAutodetector:
for i, migration in enumerate(migrations):
if i == 0 and app_leaf:
migration.dependencies.append(app_leaf)
if i == 0 and not app_leaf:
new_name = "0001_%s" % migration_name if migration_name else "0001_initial"
new_name_parts = ['%04i' % next_number]
if migration_name:
new_name_parts.append(migration_name)
elif i == 0 and not app_leaf:
new_name_parts.append('initial')
else:
new_name = "%04i_%s" % (
next_number,
migration_name or self.suggest_name(migration.operations)[:100],
)
new_name_parts.append(migration.suggest_name()[:100])
new_name = '_'.join(new_name_parts)
name_map[(app_label, migration.name)] = (app_label, new_name)
next_number += 1
migration.name = new_name
@ -1306,22 +1305,6 @@ class MigrationAutodetector:
del changes[app_label]
return changes
@classmethod
def suggest_name(cls, ops):
"""
Given a set of operations, suggest a name for the migration they might
represent. Names are not guaranteed to be unique, but put some effort
into the fallback name to avoid VCS conflicts if possible.
"""
name = None
if len(ops) == 1:
name = ops[0].migration_name_fragment
elif len(ops) > 1 and all(isinstance(o, operations.CreateModel) for o in ops):
name = '_'.join(sorted(o.migration_name_fragment for o in ops))
if name is None:
name = 'auto_%s' % get_migration_name_timestamp()
return name
@classmethod
def parse_number(cls, name):
"""

View File

@ -1,3 +1,5 @@
from django.db.migrations import operations
from django.db.migrations.utils import get_migration_name_timestamp
from django.db.transaction import atomic
from .exceptions import IrreversibleError
@ -175,6 +177,24 @@ class Migration:
operation.database_backwards(self.app_label, schema_editor, from_state, to_state)
return project_state
def suggest_name(self):
"""
Suggest a name for the operations this migration might represent. Names
are not guaranteed to be unique, but put some effort into the fallback
name to avoid VCS conflicts if possible.
"""
name = None
if len(self.operations) == 1:
name = self.operations[0].migration_name_fragment
elif (
len(self.operations) > 1 and
all(isinstance(o, operations.CreateModel) for o in self.operations)
):
name = '_'.join(sorted(o.migration_name_fragment for o in self.operations))
if name is None:
name = 'auto_%s' % get_migration_name_timestamp()
return name
class SwappableTuple(tuple):
"""

View File

@ -2481,25 +2481,39 @@ class AutodetectorTests(TestCase):
self.assertOperationAttributes(changes, 'app', 0, 1, name='book')
class AutodetectorSuggestNameTests(SimpleTestCase):
class MigrationSuggestNameTests(SimpleTestCase):
def test_single_operation(self):
ops = [migrations.CreateModel('Person', fields=[])]
self.assertEqual(MigrationAutodetector.suggest_name(ops), 'person')
ops = [migrations.DeleteModel('Person')]
self.assertEqual(MigrationAutodetector.suggest_name(ops), 'delete_person')
class Migration(migrations.Migration):
operations = [migrations.CreateModel('Person', fields=[])]
migration = Migration('0001_initial', 'test_app')
self.assertEqual(migration.suggest_name(), 'person')
class Migration(migrations.Migration):
operations = [migrations.DeleteModel('Person')]
migration = Migration('0002_initial', 'test_app')
self.assertEqual(migration.suggest_name(), 'delete_person')
def test_two_create_models(self):
ops = [
migrations.CreateModel('Person', fields=[]),
migrations.CreateModel('Animal', fields=[]),
]
self.assertEqual(MigrationAutodetector.suggest_name(ops), 'animal_person')
class Migration(migrations.Migration):
operations = [
migrations.CreateModel('Person', fields=[]),
migrations.CreateModel('Animal', fields=[]),
]
migration = Migration('0001_initial', 'test_app')
self.assertEqual(migration.suggest_name(), 'animal_person')
def test_none_name(self):
ops = [migrations.RunSQL('SELECT 1 FROM person;')]
suggest_name = MigrationAutodetector.suggest_name(ops)
class Migration(migrations.Migration):
operations = [migrations.RunSQL('SELECT 1 FROM person;')]
migration = Migration('0001_initial', 'test_app')
suggest_name = migration.suggest_name()
self.assertIs(suggest_name.startswith('auto_'), True)
def test_auto(self):
suggest_name = MigrationAutodetector.suggest_name([])
migration = migrations.Migration('0001_initial', 'test_app')
suggest_name = migration.suggest_name()
self.assertIs(suggest_name.startswith('auto_'), True)