Fixed #23302 -- Added --name/-n option to makemigrations command

This commit is contained in:
Raffaele Salmaso 2014-08-19 15:24:31 +02:00 committed by Tim Graham
parent bda2809712
commit 1435cfbe8d
7 changed files with 87 additions and 6 deletions

View File

@ -28,6 +28,8 @@ class Command(BaseCommand):
help="Create an empty migration.")
parser.add_argument('--noinput', action='store_false', dest='interactive', default=True,
help='Tells Django to NOT prompt the user for input of any kind.')
parser.add_argument('-n', '--name', action='store', dest='name', default=None,
help="Use this name for migration file(s).")
def handle(self, *app_labels, **options):
@ -36,6 +38,7 @@ class Command(BaseCommand):
self.dry_run = options.get('dry_run', False)
self.merge = options.get('merge', False)
self.empty = options.get('empty', False)
self.migration_name = options.get('name', None)
# Make sure the app they asked for exists
app_labels = set(app_labels)
@ -98,7 +101,11 @@ class Command(BaseCommand):
(app, [Migration("custom", app)])
for app in app_labels
)
changes = autodetector.arrange_for_graph(changes, loader.graph)
changes = autodetector.arrange_for_graph(
changes=changes,
graph=loader.graph,
migration_name=self.migration_name,
)
self.write_migration_files(changes)
return
@ -107,6 +114,7 @@ class Command(BaseCommand):
graph=loader.graph,
trim_to_apps=app_labels or None,
convert_apps=app_labels or None,
migration_name=self.migration_name,
)
# No changes? Tell them.

View File

@ -31,14 +31,14 @@ class MigrationAutodetector(object):
self.to_state = to_state
self.questioner = questioner or MigrationQuestioner()
def changes(self, graph, trim_to_apps=None, convert_apps=None):
def changes(self, graph, trim_to_apps=None, convert_apps=None, migration_name=None):
"""
Main entry point to produce a list of appliable changes.
Takes a graph to base names on and an optional set of apps
to try and restrict to (restriction is not guaranteed)
"""
changes = self._detect_changes(convert_apps, graph)
changes = self.arrange_for_graph(changes, graph)
changes = self.arrange_for_graph(changes, graph, migration_name)
if trim_to_apps:
changes = self._trim_to_apps(changes, trim_to_apps)
return changes
@ -951,7 +951,7 @@ class MigrationAutodetector(object):
dependencies=dependencies,
)
def arrange_for_graph(self, changes, graph):
def arrange_for_graph(self, changes, graph, migration_name=None):
"""
Takes in a result from changes() and a MigrationGraph,
and fixes the names and dependencies of the changes so they
@ -985,11 +985,11 @@ class MigrationAutodetector(object):
if i == 0 and app_leaf:
migration.dependencies.append(app_leaf)
if i == 0 and not app_leaf:
new_name = "0001_initial"
new_name = "0001_%s" % migration_name if migration_name else "0001_initial"
else:
new_name = "%04i_%s" % (
next_number,
self.suggest_name(migration.operations)[:100],
migration_name or self.suggest_name(migration.operations)[:100],
)
name_map[(app_label, migration.name)] = (app_label, new_name)
next_number += 1

View File

@ -683,6 +683,13 @@ written.
The ``--merge`` option enables fixing of migration conflicts.
.. django-admin-option:: --name, -n
.. versionadded:: 1.8
The ``--name`` option allows you to give the migration(s) a custom name instead
of a generated one.
migrate [<app_label> [<migrationname>]]
---------------------------------------

View File

@ -228,6 +228,9 @@ Management Commands
* The :djadmin:`dbshell` command now supports MySQL's optional SSL certificate
authority setting (``--ssl-ca``).
* The :djadminopt:`--name` option for :djadmin:`makemigrations` allows you to
to give the migration(s) a custom name instead of a generated one.
Models
^^^^^^

7
docs/topics/migrations.txt Executable file → Normal file
View File

@ -309,6 +309,13 @@ Note that this only works given two things:
that your database doesn't match your models, you'll just get errors when
migrations try to modify those tables.
.. versionadded:: 1.8
If you want to give the migration(s) a meaningful name instead of a generated one,
you can use the :djadminopt:`--name` option::
$ python manage.py makemigrations --name changed_my_model your_app_label
.. _historical-models:
Historical models

View File

@ -188,6 +188,31 @@ class AutodetectorTests(TestCase):
self.assertEqual(changes["otherapp"][0].name, "0001_initial")
self.assertNotIn("thirdapp", changes)
def test_custom_migration_name(self):
"Tests custom naming of migrations for graph matching."
# Make a fake graph
graph = MigrationGraph()
graph.add_node(("testapp", "0001_initial"), None)
graph.add_node(("testapp", "0002_foobar"), None)
graph.add_node(("otherapp", "0001_initial"), None)
graph.add_dependency("testapp.0002_foobar", ("testapp", "0002_foobar"), ("testapp", "0001_initial"))
# Use project state to make a new migration change set
before = self.make_project_state([])
after = self.make_project_state([self.author_empty, self.other_pony, self.other_stable])
autodetector = MigrationAutodetector(before, after)
changes = autodetector._detect_changes()
# Run through arrange_for_graph
migration_name = 'custom_name'
changes = autodetector.arrange_for_graph(changes, graph, migration_name)
# Make sure there's a new name, deps match, etc.
self.assertEqual(changes["testapp"][0].name, "0003_%s" % migration_name)
self.assertEqual(changes["testapp"][0].dependencies, [("testapp", "0002_foobar")])
self.assertEqual(changes["otherapp"][0].name, "0002_%s" % migration_name)
self.assertEqual(changes["otherapp"][0].dependencies, [("otherapp", "0001_initial")])
def test_new_model(self):
"Tests autodetection of new models"
# Make state

View File

@ -546,3 +546,34 @@ class MakeMigrationsTests(MigrationTestBase):
questioner.input = old_input
if os.path.exists(merge_file):
os.remove(merge_file)
@override_system_checks([])
def test_makemigrations_with_custom_name(self):
"""
Makes sure that makemigrations generate a custom migration.
"""
def cmd(migration_count, migration_name, *args):
with override_settings(MIGRATION_MODULES={"migrations": self.migration_pkg}):
try:
call_command("makemigrations", "migrations", "--verbosity", "0", "--name", migration_name, *args)
except CommandError:
self.fail("Makemigrations errored in creating empty migration with custom name for a proper app.")
migration_file = os.path.join(self.migration_dir, "%s_%s.py" % (migration_count, migration_name))
# Check for existing migration file in migration folder
self.assertTrue(os.path.exists(migration_file))
with codecs.open(migration_file, "r", encoding="utf-8") as fp:
content = fp.read()
self.assertTrue("# -*- coding: utf-8 -*-" in content)
content = content.replace(" ", "")
return content
# generate an initial migration
migration_name_0001 = "my_initial_migration"
content = cmd("0001", migration_name_0001)
self.assertIn("dependencies=[\n]", content)
# generate an empty migration
migration_name_0002 = "my_custom_migration"
content = cmd("0002", migration_name_0002, "--empty")
self.assertIn("dependencies=[\n('migrations','0001_%s'),\n]" % migration_name_0001, content)
self.assertIn("operations=[\n]", content)