Allow migrations in namespace packages without __init__.py

Remove obsolete __file__ check to support Python 3 implicit namespace
packages as valid migration modules. This enables migrations to work in
directories lacking __init__.py files.

Refs #29091.
This commit is contained in:
utkarsh.arya@zomato.com 2025-11-15 22:54:56 +00:00
parent 5d9cf79baf
commit 2ad994f9a0
3 changed files with 44 additions and 7 deletions

View file

@ -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)

View file

@ -190,11 +190,37 @@ class LoaderTests(TestCase):
)
def test_load_empty_dir(self):
"""
A namespace package (directory without __init__.py) can be a valid
migrations module even when it has no migration files. It's treated
the same as a regular package with no migrations.
"""
with override_settings(MIGRATION_MODULES={"migrations": "migrations.faulty_migrations.namespace"}):
loader = MigrationLoader(connection)
self.assertIn(
"migrations", loader.unmigrated_apps,
"App missing __init__.py in migrations module not in unmigrated apps."
"migrations", loader.migrated_apps,
"Namespace package should be in migrated apps even without migrations."
)
# Ensure no migrations were actually loaded
migration_count = len([
key for key in loader.disk_migrations if key[0] == "migrations"
])
self.assertEqual(migration_count, 0)
@override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_namespace_package"})
def test_namespace_package_with_migrations(self):
"""
Namespace packages (directories without __init__.py) can contain
migration files and should be loaded correctly.
"""
loader = MigrationLoader(connection)
self.assertIn(
"migrations", loader.migrated_apps,
"Namespace package with migrations should be in migrated apps."
)
self.assertIn(
("migrations", "0001_initial"), loader.disk_migrations,
"Migration from namespace package should be loaded."
)
@override_settings(

View file

@ -0,0 +1,16 @@
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)),
],
),
]