summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaffaele Salmaso <raffaele@salmaso.org>2014-08-19 15:24:31 +0200
committerTim Graham <timograham@gmail.com>2014-09-02 21:09:18 -0400
commit1435cfbe8dbd85c17c9a25af99f910c407dfa9bd (patch)
tree7b3902cec1c408336b6b908653fb02cecd2a47ee
parentbda28097126c0ff77c51d598ccc19663bcd3ee95 (diff)
Fixed #23302 -- Added --name/-n option to makemigrations command
-rw-r--r--django/core/management/commands/makemigrations.py10
-rw-r--r--django/db/migrations/autodetector.py10
-rw-r--r--docs/ref/django-admin.txt7
-rw-r--r--docs/releases/1.8.txt3
-rw-r--r--[-rwxr-xr-x]docs/topics/migrations.txt7
-rw-r--r--tests/migrations/test_autodetector.py25
-rw-r--r--tests/migrations/test_commands.py31
7 files changed, 87 insertions, 6 deletions
diff --git a/django/core/management/commands/makemigrations.py b/django/core/management/commands/makemigrations.py
index b1a9e0c017..96a0189ebb 100644
--- a/django/core/management/commands/makemigrations.py
+++ b/django/core/management/commands/makemigrations.py
@@ -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.
diff --git a/django/db/migrations/autodetector.py b/django/db/migrations/autodetector.py
index 4a837fbba1..41884058b7 100644
--- a/django/db/migrations/autodetector.py
+++ b/django/db/migrations/autodetector.py
@@ -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
diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt
index fd80080f64..e91423ff6b 100644
--- a/docs/ref/django-admin.txt
+++ b/docs/ref/django-admin.txt
@@ -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>]]
---------------------------------------
diff --git a/docs/releases/1.8.txt b/docs/releases/1.8.txt
index 8b1df14418..a95024420c 100644
--- a/docs/releases/1.8.txt
+++ b/docs/releases/1.8.txt
@@ -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
^^^^^^
diff --git a/docs/topics/migrations.txt b/docs/topics/migrations.txt
index cd03a2f840..626790183b 100755..100644
--- a/docs/topics/migrations.txt
+++ b/docs/topics/migrations.txt
@@ -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
diff --git a/tests/migrations/test_autodetector.py b/tests/migrations/test_autodetector.py
index c3bec6ba84..42569d354d 100644
--- a/tests/migrations/test_autodetector.py
+++ b/tests/migrations/test_autodetector.py
@@ -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
diff --git a/tests/migrations/test_commands.py b/tests/migrations/test_commands.py
index 00b7f65ab8..798dd86fe5 100644
--- a/tests/migrations/test_commands.py
+++ b/tests/migrations/test_commands.py
@@ -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)