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)),
+            ],
+        ),
+    ]