From 584110417f6a3c16cc0c6723825a21940a9a2bc4 Mon Sep 17 00:00:00 2001 From: Loic Bistuer Date: Sat, 19 Oct 2013 07:24:38 +0700 Subject: [PATCH] Fixed #21283 -- Added support for migrations if models is a package. Thanks Markus Holtermann for the report. --- django/db/migrations/loader.py | 4 +-- django/db/migrations/writer.py | 18 ++++++----- django/db/models/loading.py | 7 +++++ .../migrations_test_apps/__init__.py | 0 .../migrations_test_apps/normal/__init__.py | 0 .../migrations_test_apps/normal/models.py | 0 .../with_package_model/__init__.py | 0 .../with_package_model/models/__init__.py | 0 tests/migrations/test_writer.py | 30 +++++++++++++++++-- 9 files changed, 46 insertions(+), 13 deletions(-) create mode 100644 tests/migrations/migrations_test_apps/__init__.py create mode 100644 tests/migrations/migrations_test_apps/normal/__init__.py create mode 100644 tests/migrations/migrations_test_apps/normal/models.py create mode 100644 tests/migrations/migrations_test_apps/with_package_model/__init__.py create mode 100644 tests/migrations/migrations_test_apps/with_package_model/models/__init__.py diff --git a/django/db/migrations/loader.py b/django/db/migrations/loader.py index b40584981d..c9be3841b9 100644 --- a/django/db/migrations/loader.py +++ b/django/db/migrations/loader.py @@ -41,8 +41,8 @@ class MigrationLoader(object): def migrations_module(cls, app_label): if app_label in settings.MIGRATION_MODULES: return settings.MIGRATION_MODULES[app_label] - app = cache.get_app(app_label) - return ".".join(app.__name__.split(".")[:-1] + ["migrations"]) + else: + return '%s.migrations' % cache.get_app_package(app_label) def load_disk(self): """ diff --git a/django/db/migrations/writer.py b/django/db/migrations/writer.py index beb1b30aba..333f39d6e0 100644 --- a/django/db/migrations/writer.py +++ b/django/db/migrations/writer.py @@ -61,20 +61,22 @@ class MigrationWriter(object): @property def path(self): - migrations_module_name = MigrationLoader.migrations_module(self.migration.app_label) - app_module = cache.get_app(self.migration.app_label) + migrations_package_name = MigrationLoader.migrations_module(self.migration.app_label) # See if we can import the migrations module directly try: - migrations_module = import_module(migrations_module_name) + migrations_module = import_module(migrations_package_name) basedir = os.path.dirname(migrations_module.__file__) except ImportError: + app = cache.get_app(self.migration.app_label) + app_path = cache._get_app_path(app) + app_package_name = cache._get_app_package(app) + migrations_package_basename = migrations_package_name.split(".")[-1] + # Alright, see if it's a direct submodule of the app - oneup = ".".join(migrations_module_name.split(".")[:-1]) - app_oneup = ".".join(app_module.__name__.split(".")[:-1]) - if oneup == app_oneup: - basedir = os.path.join(os.path.dirname(app_module.__file__), migrations_module_name.split(".")[-1]) + if '%s.%s' % (app_package_name, migrations_package_basename) == migrations_package_name: + basedir = os.path.join(app_path, migrations_package_basename) else: - raise ImportError("Cannot open migrations module %s for app %s" % (migrations_module_name, self.migration.app_label)) + raise ImportError("Cannot open migrations module %s for app %s" % (migrations_package_name, self.migration.app_label)) return os.path.join(basedir, self.filename) @classmethod diff --git a/django/db/models/loading.py b/django/db/models/loading.py index 2858b8b699..162f334846 100644 --- a/django/db/models/loading.py +++ b/django/db/models/loading.py @@ -185,6 +185,12 @@ class BaseAppCache(object): return [elt[0] for elt in apps] + def _get_app_package(self, app): + return '.'.join(app.__name__.split('.')[:-1]) + + def get_app_package(self, app_label): + return self._get_app_package(self.get_app(app_label)) + def _get_app_path(self, app): if hasattr(app, '__path__'): # models/__init__.py package app_path = app.__path__[0] @@ -380,6 +386,7 @@ cache = AppCache() # These methods were always module level, so are kept that way for backwards # compatibility. get_apps = cache.get_apps +get_app_package = cache.get_app_package get_app_path = cache.get_app_path get_app_paths = cache.get_app_paths get_app = cache.get_app diff --git a/tests/migrations/migrations_test_apps/__init__.py b/tests/migrations/migrations_test_apps/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/migrations/migrations_test_apps/normal/__init__.py b/tests/migrations/migrations_test_apps/normal/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/migrations/migrations_test_apps/normal/models.py b/tests/migrations/migrations_test_apps/normal/models.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/migrations/migrations_test_apps/with_package_model/__init__.py b/tests/migrations/migrations_test_apps/with_package_model/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/migrations/migrations_test_apps/with_package_model/models/__init__.py b/tests/migrations/migrations_test_apps/with_package_model/models/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/migrations/test_writer.py b/tests/migrations/test_writer.py index bf9f55aff0..dbee0d1130 100644 --- a/tests/migrations/test_writer.py +++ b/tests/migrations/test_writer.py @@ -2,12 +2,15 @@ from __future__ import unicode_literals +import copy import datetime +import os -from django.utils import six -from django.test import TestCase -from django.db.migrations.writer import MigrationWriter from django.db import models, migrations +from django.db.migrations.writer import MigrationWriter +from django.db.models.loading import cache +from django.test import TestCase, override_settings +from django.utils import six from django.utils.translation import ugettext_lazy as _ @@ -95,3 +98,24 @@ class WriterTests(TestCase): # Just make sure it runs for now, and that things look alright. result = self.safe_exec(output) self.assertIn("Migration", result) + + def test_migration_path(self): + _old_app_store = copy.deepcopy(cache.app_store) + + test_apps = [ + 'migrations.migrations_test_apps.normal', + 'migrations.migrations_test_apps.with_package_model', + ] + + base_dir = os.path.dirname(os.path.dirname(__file__)) + + try: + with override_settings(INSTALLED_APPS=test_apps): + for app in test_apps: + cache.load_app(app) + migration = migrations.Migration('0001_initial', app.split('.')[-1]) + expected_path = os.path.join(base_dir, *(app.split('.') + ['migrations', '0001_initial.py'])) + writer = MigrationWriter(migration) + self.assertEqual(writer.path, expected_path) + finally: + cache.app_store = _old_app_store