mirror of
https://github.com/django/django.git
synced 2024-12-22 17:16:24 +00:00
Made it possible to create apps without a models module.
This commit revertsf44c4a5d0f
and39bbd165
. django.test.simple will be updated in a separate commit as it requires invasive changes.
This commit is contained in:
parent
69039becde
commit
5ba743e262
@ -22,7 +22,8 @@ class AppConfig(object):
|
||||
self.app_module = app_module
|
||||
|
||||
# Module containing models eg. <module 'django.contrib.admin.models'
|
||||
# from 'django/contrib/admin/models.pyc'>.
|
||||
# from 'django/contrib/admin/models.pyc'>. None if the application
|
||||
# doesn't have a models module.
|
||||
self.models_module = models_module
|
||||
|
||||
# Mapping of lower case model names to model classes.
|
||||
|
@ -88,7 +88,7 @@ class BaseAppCache(object):
|
||||
for app_name in settings.INSTALLED_APPS:
|
||||
if app_name in self.handled:
|
||||
continue
|
||||
self.load_app(app_name, True)
|
||||
self.load_app(app_name, can_postpone=True)
|
||||
if not self.nesting_level:
|
||||
for app_name in self.postponed:
|
||||
self.load_app(app_name)
|
||||
@ -115,10 +115,10 @@ class BaseAppCache(object):
|
||||
models_module = import_module('%s.%s' % (app_name, MODELS_MODULE_NAME))
|
||||
except ImportError:
|
||||
self.nesting_level -= 1
|
||||
# If the app doesn't have a models module, we can just ignore the
|
||||
# ImportError and return no models for it.
|
||||
# If the app doesn't have a models module, we can just swallow the
|
||||
# ImportError and return no models for this app.
|
||||
if not module_has_submodule(app_module, MODELS_MODULE_NAME):
|
||||
return None
|
||||
models_module = None
|
||||
# But if the app does have a models module, we need to figure out
|
||||
# whether to suppress or propagate the error. If can_postpone is
|
||||
# True then it may be that the package is still being imported by
|
||||
@ -129,7 +129,7 @@ class BaseAppCache(object):
|
||||
else:
|
||||
if can_postpone:
|
||||
self.postponed.append(app_name)
|
||||
return None
|
||||
return
|
||||
else:
|
||||
raise
|
||||
|
||||
@ -154,22 +154,27 @@ class BaseAppCache(object):
|
||||
"""
|
||||
return self.loaded
|
||||
|
||||
def get_app_configs(self, only_installed=True):
|
||||
def get_app_configs(self, only_installed=True, only_with_models_module=False):
|
||||
"""
|
||||
Return an iterable of application configurations.
|
||||
|
||||
If only_installed is True (default), only applications explicitly
|
||||
listed in INSTALLED_APPS are considered.
|
||||
|
||||
If only_with_models_module in True (non-default), only applications
|
||||
containing a models module are considered.
|
||||
"""
|
||||
self.populate()
|
||||
for app_config in self.app_configs.values():
|
||||
if only_installed and not app_config.installed:
|
||||
continue
|
||||
if only_with_models_module and app_config.models_module is None:
|
||||
continue
|
||||
if self.available_apps is not None and app_config.name not in self.available_apps:
|
||||
continue
|
||||
yield app_config
|
||||
|
||||
def get_app_config(self, app_label, only_installed=True):
|
||||
def get_app_config(self, app_label, only_installed=True, only_with_models_module=False):
|
||||
"""
|
||||
Returns the application configuration for the given app_label.
|
||||
|
||||
@ -180,11 +185,18 @@ class BaseAppCache(object):
|
||||
|
||||
If only_installed is True (default), only applications explicitly
|
||||
listed in INSTALLED_APPS are considered.
|
||||
|
||||
If only_with_models_module in True (non-default), only applications
|
||||
containing a models module are considered.
|
||||
"""
|
||||
self.populate()
|
||||
app_config = self.app_configs.get(app_label)
|
||||
if app_config is None or (only_installed and not app_config.installed):
|
||||
if app_config is None:
|
||||
raise LookupError("No app with label %r." % app_label)
|
||||
if only_installed and not app_config.installed:
|
||||
raise LookupError("App with label %r isn't in INSTALLED_APPS." % app_label)
|
||||
if only_with_models_module and app_config.models_module is None:
|
||||
raise LookupError("App with label %r doesn't have a models module." % app_label)
|
||||
if self.available_apps is not None and app_config.name not in self.available_apps:
|
||||
raise UnavailableApp("App with label %r isn't available." % app_label)
|
||||
return app_config
|
||||
|
@ -86,7 +86,7 @@ If you're unsure, answer 'no'.
|
||||
|
||||
|
||||
def update_all_contenttypes(verbosity=2, **kwargs):
|
||||
for app_config in app_cache.get_app_configs():
|
||||
for app_config in app_cache.get_app_configs(only_with_models_module=True):
|
||||
update_contenttypes(app_config.models_module, None, verbosity, **kwargs)
|
||||
|
||||
signals.post_migrate.connect(update_contenttypes)
|
||||
|
@ -345,12 +345,16 @@ class AppCommand(BaseCommand):
|
||||
if not app_labels:
|
||||
raise CommandError('Enter at least one appname.')
|
||||
try:
|
||||
app_list = [app_cache.get_app_config(app_label).models_module for app_label in app_labels]
|
||||
app_configs = [app_cache.get_app_config(app_label) for app_label in app_labels]
|
||||
except (LookupError, ImportError) as e:
|
||||
raise CommandError("%s. Are you sure your INSTALLED_APPS setting is correct?" % e)
|
||||
output = []
|
||||
for app in app_list:
|
||||
app_output = self.handle_app(app, **options)
|
||||
for app_config in app_configs:
|
||||
if app_config.models_module is None:
|
||||
raise CommandError(
|
||||
"AppCommand cannot handle app %r because it doesn't have "
|
||||
"a models module." % app_config.label)
|
||||
app_output = self.handle_app(app_config.models_module, **options)
|
||||
if app_output:
|
||||
output.append(app_output)
|
||||
return '\n'.join(output)
|
||||
|
@ -70,6 +70,7 @@ class Command(BaseCommand):
|
||||
else:
|
||||
try:
|
||||
app_obj = app_cache.get_app_config(exclude).models_module
|
||||
if app_obj is not None:
|
||||
excluded_apps.add(app_obj)
|
||||
except LookupError:
|
||||
raise CommandError('Unknown app in excludes: %s' % exclude)
|
||||
@ -78,7 +79,7 @@ class Command(BaseCommand):
|
||||
if primary_keys:
|
||||
raise CommandError("You can only use --pks option with one model")
|
||||
app_list = OrderedDict((app_config.models_module, None)
|
||||
for app_config in app_cache.get_app_configs()
|
||||
for app_config in app_cache.get_app_configs(only_with_models_module=True)
|
||||
if app_config.models_module not in excluded_apps)
|
||||
else:
|
||||
if len(app_labels) > 1 and primary_keys:
|
||||
@ -91,7 +92,7 @@ class Command(BaseCommand):
|
||||
app = app_cache.get_app_config(app_label).models_module
|
||||
except LookupError:
|
||||
raise CommandError("Unknown application: %s" % app_label)
|
||||
if app in excluded_apps:
|
||||
if app is None or app in excluded_apps:
|
||||
continue
|
||||
model = app_cache.get_model(app_label, model_label)
|
||||
if model is None:
|
||||
@ -111,7 +112,7 @@ class Command(BaseCommand):
|
||||
app = app_cache.get_app_config(app_label).models_module
|
||||
except LookupError:
|
||||
raise CommandError("Unknown application: %s" % app_label)
|
||||
if app in excluded_apps:
|
||||
if app is None or app in excluded_apps:
|
||||
continue
|
||||
app_list[app] = None
|
||||
|
||||
|
@ -94,6 +94,6 @@ Are you sure you want to do this?
|
||||
# Emit the post migrate signal. This allows individual applications to
|
||||
# respond as if the database had been migrated from scratch.
|
||||
all_models = []
|
||||
for app_config in app_cache.get_app_configs():
|
||||
for app_config in app_cache.get_app_configs(only_with_models_module=True):
|
||||
all_models.extend(router.get_migratable_models(app_config.models_module, database, include_auto_created=True))
|
||||
emit_post_migrate_signal(set(all_models), verbosity, interactive, database)
|
||||
|
@ -182,7 +182,7 @@ class Command(BaseCommand):
|
||||
all_models = [
|
||||
(app_config.label,
|
||||
router.get_migratable_models(app_config.models_module, connection.alias, include_auto_created=True))
|
||||
for app_config in app_cache.get_app_configs()
|
||||
for app_config in app_cache.get_app_configs(only_with_models_module=True)
|
||||
if app_config.label in apps
|
||||
]
|
||||
|
||||
|
@ -207,7 +207,7 @@ def custom_sql_for_model(model, style, connection):
|
||||
|
||||
def emit_pre_migrate_signal(create_models, verbosity, interactive, db):
|
||||
# Emit the pre_migrate signal for every application.
|
||||
for app_config in app_cache.get_app_configs():
|
||||
for app_config in app_cache.get_app_configs(only_with_models_module=True):
|
||||
if verbosity >= 2:
|
||||
print("Running pre-migrate handlers for application %s" % app_config.label)
|
||||
models.signals.pre_migrate.send(
|
||||
@ -221,7 +221,7 @@ def emit_pre_migrate_signal(create_models, verbosity, interactive, db):
|
||||
|
||||
def emit_post_migrate_signal(created_models, verbosity, interactive, db):
|
||||
# Emit the post_migrate signal for every application.
|
||||
for app_config in app_cache.get_app_configs():
|
||||
for app_config in app_cache.get_app_configs(only_with_models_module=True):
|
||||
if verbosity >= 2:
|
||||
print("Running post-migrate handlers for application %s" % app_config.label)
|
||||
models.signals.post_migrate.send(
|
||||
|
@ -1271,7 +1271,7 @@ class BaseDatabaseIntrospection(object):
|
||||
from django.apps import app_cache
|
||||
from django.db import router
|
||||
tables = set()
|
||||
for app_config in app_cache.get_app_configs():
|
||||
for app_config in app_cache.get_app_configs(only_with_models_module=True):
|
||||
for model in router.get_migratable_models(app_config.models_module, self.connection.alias):
|
||||
if not model._meta.managed:
|
||||
continue
|
||||
@ -1292,7 +1292,7 @@ class BaseDatabaseIntrospection(object):
|
||||
from django.apps import app_cache
|
||||
from django.db import router
|
||||
all_models = []
|
||||
for app_config in app_cache.get_app_configs():
|
||||
for app_config in app_cache.get_app_configs(only_with_models_module=True):
|
||||
all_models.extend(router.get_migratable_models(app_config.models_module, self.connection.alias))
|
||||
tables = list(map(self.table_name_converter, tables))
|
||||
return set([
|
||||
@ -1307,7 +1307,7 @@ class BaseDatabaseIntrospection(object):
|
||||
|
||||
sequence_list = []
|
||||
|
||||
for app_config in app_cache.get_app_configs():
|
||||
for app_config in app_cache.get_app_configs(only_with_models_module=True):
|
||||
for model in router.get_migratable_models(app_config.models_module, self.connection.alias):
|
||||
if not model._meta.managed:
|
||||
continue
|
||||
|
@ -55,7 +55,7 @@ class MigrationLoader(object):
|
||||
self.disk_migrations = {}
|
||||
self.unmigrated_apps = set()
|
||||
self.migrated_apps = set()
|
||||
for app_config in app_cache.get_app_configs():
|
||||
for app_config in app_cache.get_app_configs(only_with_models_module=True):
|
||||
# Get the migrations module directory
|
||||
module_name = self.migrations_module(app_config.label)
|
||||
was_loaded = module_name in sys.modules
|
||||
|
@ -1,6 +0,0 @@
|
||||
from django.test import TestCase
|
||||
|
||||
|
||||
class NoModelTests(TestCase):
|
||||
""" A placeholder test case. See empty.tests for more info. """
|
||||
pass
|
@ -1,7 +1,4 @@
|
||||
from django.apps import app_cache
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.utils import six
|
||||
|
||||
from .models import Empty
|
||||
|
||||
@ -16,20 +13,3 @@ class EmptyModelTests(TestCase):
|
||||
self.assertTrue(m.id is not None)
|
||||
existing = Empty(m.id)
|
||||
existing.save()
|
||||
|
||||
|
||||
class NoModelTests(TestCase):
|
||||
"""
|
||||
Test for #7198 to ensure that the proper error message is raised
|
||||
when attempting to load an app with no models.py file.
|
||||
|
||||
Because the test runner won't currently load a test module with no
|
||||
models.py file, this TestCase instead lives in this module.
|
||||
|
||||
It seemed like an appropriate home for it.
|
||||
"""
|
||||
@override_settings(INSTALLED_APPS=("empty.no_models",))
|
||||
def test_no_models(self):
|
||||
with six.assertRaisesRegex(self, LookupError,
|
||||
"No app with label 'no_models'."):
|
||||
app_cache.get_app_config('no_models')
|
||||
|
0
tests/no_models/__init__.py
Normal file
0
tests/no_models/__init__.py
Normal file
10
tests/no_models/tests.py
Normal file
10
tests/no_models/tests.py
Normal file
@ -0,0 +1,10 @@
|
||||
from django.apps import app_cache
|
||||
from django.test import TestCase
|
||||
|
||||
|
||||
class NoModelTests(TestCase):
|
||||
|
||||
def test_no_models(self):
|
||||
"""Test that it's possible to load an app with no models.py file."""
|
||||
app_config = app_cache.get_app_config('no_models')
|
||||
self.assertIsNone(app_config.models_module)
|
Loading…
Reference in New Issue
Block a user