From e4eae5df0ec222fa13d6bac226b3b10c75f3d40d Mon Sep 17 00:00:00 2001 From: Moayad Mardini Date: Fri, 30 May 2014 01:23:09 +0300 Subject: [PATCH] [1.7.x] Fixed #22682 -- `makemigrations` will create `MIGRATION_MODULES` package `makemigrations` will automatically create the package specified in `MIGRATION_MODULES` if it doesn't already exist. Thanks ovidiuc4 for the report. --- django/db/migrations/writer.py | 14 ++++++++++++- docs/ref/settings.txt | 3 +++ tests/migrations/test_commands.py | 35 +++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/django/db/migrations/writer.py b/django/db/migrations/writer.py index c8a3a6cf76..f73c4b78a0 100644 --- a/django/db/migrations/writer.py +++ b/django/db/migrations/writer.py @@ -6,6 +6,7 @@ import decimal import collections from importlib import import_module import os +import sys import types from django.apps import apps @@ -158,7 +159,18 @@ class MigrationWriter(object): if '%s.%s' % (app_config.name, migrations_package_basename) == migrations_package_name: basedir = os.path.join(app_config.path, migrations_package_basename) else: - raise ImportError("Cannot open migrations module %s for app %s" % (migrations_package_name, self.migration.app_label)) + # In case of using MIGRATION_MODULES setting and the custom + # package doesn't exist, create one. + package_dirs = migrations_package_name.split(".") + create_path = os.path.join(sys.path[0], *package_dirs) + if not os.path.isdir(create_path): + os.makedirs(create_path) + for i in range(1, len(package_dirs) + 1): + init_dir = os.path.join(sys.path[0], *package_dirs[:i]) + init_path = os.path.join(init_dir, "__init__.py") + if not os.path.isfile(init_path): + open(init_path, "w").close() + return os.path.join(create_path, self.filename) return os.path.join(basedir, self.filename) @classmethod diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index 7fbc6324e5..c695131565 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -1772,6 +1772,9 @@ Example:: In this case, migrations pertaining to the ``blog`` app will be contained in the ``blog.db_migrations`` package. +:djadmin:`makemigrations` will automatically create the package if it doesn't +already exist. + .. setting:: MONTH_DAY_FORMAT MONTH_DAY_FORMAT diff --git a/tests/migrations/test_commands.py b/tests/migrations/test_commands.py index 4783ffa0c3..4063a8db8e 100644 --- a/tests/migrations/test_commands.py +++ b/tests/migrations/test_commands.py @@ -413,3 +413,38 @@ class MakeMigrationsTests(MigrationTestBase): self.assertIn("migrations.AddField(", stdout.getvalue()) self.assertIn("model_name='sillymodel',", stdout.getvalue()) self.assertIn("name='silly_char',", stdout.getvalue()) + + @override_system_checks([]) + @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_path_doesnt_exist.foo.bar"}) + def test_makemigrations_migrations_modules_path_not_exist(self): + """ + Ticket #22682 -- Makemigrations fails when specifying custom location + for migration files (using MIGRATION_MODULES) if the custom path + doesn't already exist. + """ + + class SillyModel(models.Model): + silly_field = models.BooleanField(default=False) + + class Meta: + app_label = "migrations" + + stdout = six.StringIO() + call_command("makemigrations", "migrations", stdout=stdout) + + # Command output indicates the migration is created. + self.assertIn(" - Create model SillyModel", stdout.getvalue()) + + # Migrations file is actually created in the expected path. + self.assertTrue(os.path.isfile(os.path.join(self.test_dir, + "test_migrations_path_doesnt_exist", "foo", "bar", + "0001_initial.py"))) + + os.chdir(self.test_dir) + try: + self._rmrf(os.path.join(self.test_dir, + "test_migrations_path_doesnt_exist")) + pass + except OSError: + pass + os.chdir(self._cwd)