Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #21283 -- Added support for migrations if models is a package.

Thanks Markus Holtermann for the report.
  • Loading branch information...
commit 584110417f6a3c16cc0c6723825a21940a9a2bc4 1 parent 96d1d4e
Loic Bistuer authored timgraham committed
4  django/db/migrations/loader.py
@@ -41,8 +41,8 @@ def __init__(self, connection):
41 41
     def migrations_module(cls, app_label):
42 42
         if app_label in settings.MIGRATION_MODULES:
43 43
             return settings.MIGRATION_MODULES[app_label]
44  
-        app = cache.get_app(app_label)
45  
-        return ".".join(app.__name__.split(".")[:-1] + ["migrations"])
  44
+        else:
  45
+            return '%s.migrations' % cache.get_app_package(app_label)
46 46
 
47 47
     def load_disk(self):
48 48
         """
18  django/db/migrations/writer.py
@@ -61,20 +61,22 @@ def filename(self):
61 61
 
62 62
     @property
63 63
     def path(self):
64  
-        migrations_module_name = MigrationLoader.migrations_module(self.migration.app_label)
65  
-        app_module = cache.get_app(self.migration.app_label)
  64
+        migrations_package_name = MigrationLoader.migrations_module(self.migration.app_label)
66 65
         # See if we can import the migrations module directly
67 66
         try:
68  
-            migrations_module = import_module(migrations_module_name)
  67
+            migrations_module = import_module(migrations_package_name)
69 68
             basedir = os.path.dirname(migrations_module.__file__)
70 69
         except ImportError:
  70
+            app = cache.get_app(self.migration.app_label)
  71
+            app_path = cache._get_app_path(app)
  72
+            app_package_name = cache._get_app_package(app)
  73
+            migrations_package_basename = migrations_package_name.split(".")[-1]
  74
+
71 75
             # Alright, see if it's a direct submodule of the app
72  
-            oneup = ".".join(migrations_module_name.split(".")[:-1])
73  
-            app_oneup = ".".join(app_module.__name__.split(".")[:-1])
74  
-            if oneup == app_oneup:
75  
-                basedir = os.path.join(os.path.dirname(app_module.__file__), migrations_module_name.split(".")[-1])
  76
+            if '%s.%s' % (app_package_name, migrations_package_basename) == migrations_package_name:
  77
+                basedir = os.path.join(app_path, migrations_package_basename)
76 78
             else:
77  
-                raise ImportError("Cannot open migrations module %s for app %s" % (migrations_module_name, self.migration.app_label))
  79
+                raise ImportError("Cannot open migrations module %s for app %s" % (migrations_package_name, self.migration.app_label))
78 80
         return os.path.join(basedir, self.filename)
79 81
 
80 82
     @classmethod
7  django/db/models/loading.py
@@ -185,6 +185,12 @@ def get_apps(self):
185 185
 
186 186
         return [elt[0] for elt in apps]
187 187
 
  188
+    def _get_app_package(self, app):
  189
+        return '.'.join(app.__name__.split('.')[:-1])
  190
+
  191
+    def get_app_package(self, app_label):
  192
+        return self._get_app_package(self.get_app(app_label))
  193
+
188 194
     def _get_app_path(self, app):
189 195
         if hasattr(app, '__path__'):        # models/__init__.py package
190 196
             app_path = app.__path__[0]
@@ -380,6 +386,7 @@ def __init__(self):
380 386
 # These methods were always module level, so are kept that way for backwards
381 387
 # compatibility.
382 388
 get_apps = cache.get_apps
  389
+get_app_package = cache.get_app_package
383 390
 get_app_path = cache.get_app_path
384 391
 get_app_paths = cache.get_app_paths
385 392
 get_app = cache.get_app
0  tests/migrations/migrations_test_apps/__init__.py
No changes.
0  tests/migrations/migrations_test_apps/normal/__init__.py
No changes.
0  tests/migrations/migrations_test_apps/normal/models.py
No changes.
0  tests/migrations/migrations_test_apps/with_package_model/__init__.py
No changes.
0  tests/migrations/migrations_test_apps/with_package_model/models/__init__.py
No changes.
30  tests/migrations/test_writer.py
@@ -2,12 +2,15 @@
2 2
 
3 3
 from __future__ import unicode_literals
4 4
 
  5
+import copy
5 6
 import datetime
  7
+import os
6 8
 
7  
-from django.utils import six
8  
-from django.test import TestCase
9  
-from django.db.migrations.writer import MigrationWriter
10 9
 from django.db import models, migrations
  10
+from django.db.migrations.writer import MigrationWriter
  11
+from django.db.models.loading import cache
  12
+from django.test import TestCase, override_settings
  13
+from django.utils import six
11 14
 from django.utils.translation import ugettext_lazy as _
12 15
 
13 16
 
@@ -95,3 +98,24 @@ def test_simple_migration(self):
95 98
         # Just make sure it runs for now, and that things look alright.
96 99
         result = self.safe_exec(output)
97 100
         self.assertIn("Migration", result)
  101
+
  102
+    def test_migration_path(self):
  103
+        _old_app_store = copy.deepcopy(cache.app_store)
  104
+
  105
+        test_apps = [
  106
+            'migrations.migrations_test_apps.normal',
  107
+            'migrations.migrations_test_apps.with_package_model',
  108
+        ]
  109
+
  110
+        base_dir = os.path.dirname(os.path.dirname(__file__))
  111
+
  112
+        try:
  113
+            with override_settings(INSTALLED_APPS=test_apps):
  114
+                for app in test_apps:
  115
+                    cache.load_app(app)
  116
+                    migration = migrations.Migration('0001_initial', app.split('.')[-1])
  117
+                    expected_path = os.path.join(base_dir, *(app.split('.') + ['migrations', '0001_initial.py']))
  118
+                    writer = MigrationWriter(migration)
  119
+                    self.assertEqual(writer.path, expected_path)
  120
+        finally:
  121
+            cache.app_store = _old_app_store

0 notes on commit 5841104

Please sign in to comment.
Something went wrong with that request. Please try again.