diff --git a/django/db/migrations/loader.py b/django/db/migrations/loader.py index 8c308621d2..6be8048299 100644 --- a/django/db/migrations/loader.py +++ b/django/db/migrations/loader.py @@ -84,11 +84,6 @@ class MigrationLoader: continue raise else: - # Empty directories are namespaces. - # getattr() needed on PY36 and older (replace w/attribute access). - if getattr(module, '__file__', None) is None: - self.unmigrated_apps.add(app_config.label) - continue # Module is not a package (e.g. migrations.py). if not hasattr(module, '__path__'): self.unmigrated_apps.add(app_config.label) @@ -96,11 +91,14 @@ class MigrationLoader: # Force a reload if it's already loaded (tests need this) if was_loaded: reload(module) - self.migrated_apps.add(app_config.label) migration_names = { name for _, name, is_pkg in pkgutil.iter_modules(module.__path__) if not is_pkg and name[0] not in '_~' } + if migration_names or self.ignore_no_migrations: + self.migrated_apps.add(app_config.label) + else: + self.unmigrated_apps.add(app_config.label) # Load migrations for migration_name in migration_names: migration_path = '%s.%s' % (module_name, migration_name) diff --git a/docs/releases/3.1.txt b/docs/releases/3.1.txt index 6b3fa0b49e..7d1948b183 100644 --- a/docs/releases/3.1.txt +++ b/docs/releases/3.1.txt @@ -155,7 +155,8 @@ Management Commands Migrations ~~~~~~~~~~ -* ... +* Migrations are now loaded also from directories without ``__init__.py`` + files. Models ~~~~~~ diff --git a/tests/migrations/test_loader.py b/tests/migrations/test_loader.py index 0f0a590e7c..7bc9ccabd4 100644 --- a/tests/migrations/test_loader.py +++ b/tests/migrations/test_loader.py @@ -508,6 +508,17 @@ class LoaderTests(TestCase): migrations = [name for app, name in loader.disk_migrations if app == 'migrations'] self.assertEqual(migrations, ['0001_initial']) + @override_settings( + MIGRATION_MODULES={'migrations': 'migrations.test_migrations_namespace_package'}, + ) + def test_loading_namespace_package(self): + """Migration directories without an __init__.py file are loaded.""" + migration_loader = MigrationLoader(connection) + self.assertEqual( + migration_loader.graph.forwards_plan(('migrations', '0001_initial')), + [('migrations', '0001_initial')], + ) + class PycLoaderTests(MigrationTestBase): diff --git a/tests/migrations/test_migrations_namespace_package/0001_initial.py b/tests/migrations/test_migrations_namespace_package/0001_initial.py new file mode 100644 index 0000000000..34c73ea086 --- /dev/null +++ b/tests/migrations/test_migrations_namespace_package/0001_initial.py @@ -0,0 +1,15 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + initial = True + + operations = [ + migrations.CreateModel( + "Author", + [ + ("id", models.AutoField(primary_key=True)), + ("name", models.CharField(max_length=255)), + ], + ), + ]