diff --git a/django/contrib/auth/__init__.py b/django/contrib/auth/__init__.py index d5d4430dc0..065eaddd59 100644 --- a/django/contrib/auth/__init__.py +++ b/django/contrib/auth/__init__.py @@ -97,7 +97,10 @@ def get_user_model(): from django.conf import settings from django.db.models import get_model - app_label, model_name = settings.AUTH_USER_MODEL.split('.') + try: + app_label, model_name = settings.AUTH_USER_MODEL.split('.') + except ValueError: + raise ImproperlyConfigured("AUTH_USER_MODEL must be of the form 'app_label.model_name'") return get_model(app_label, model_name) diff --git a/django/core/management/validation.py b/django/core/management/validation.py index 462d1ec0b6..fa3edb4430 100644 --- a/django/core/management/validation.py +++ b/django/core/management/validation.py @@ -285,6 +285,16 @@ def get_validation_errors(outfile, app=None): if r.get_accessor_name() == rel_query_name: e.add(opts, "Reverse query name for m2m field '%s' clashes with related field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name)) + # Check swappable attribute. + if opts.swapped: + try: + app_label, model_name = opts.swapped.split('.') + except ValueError: + e.add(opts, "%s is not of the form 'app_label.app_name'." % opts.swappable) + continue + if not models.get_model(app_label, model_name): + e.add(opts, "Model has been swapped out for '%s' which has not been installed or is abstract." % opts.swapped) + # Check ordering attribute. if opts.ordering: for field_name in opts.ordering: diff --git a/tests/modeltests/invalid_models/invalid_models/models.py b/tests/modeltests/invalid_models/invalid_models/models.py index 6b2d6bd603..3f95d314e3 100644 --- a/tests/modeltests/invalid_models/invalid_models/models.py +++ b/tests/modeltests/invalid_models/invalid_models/models.py @@ -310,6 +310,26 @@ class SwappedModel(models.Model): swappable = 'TEST_SWAPPED_MODEL' +class BadSwappableValue(models.Model): + """A model that can be swapped out; during testing, the swappable + value is not of the format app.model + """ + name = models.CharField(max_length=100) + + class Meta: + swappable = 'TEST_SWAPPED_MODEL_BAD_VALUE' + + +class BadSwappableModel(models.Model): + """A model that can be swapped out; during testing, the swappable + value references an unknown model. + """ + name = models.CharField(max_length=100) + + class Meta: + swappable = 'TEST_SWAPPED_MODEL_BAD_MODEL' + + class HardReferenceModel(models.Model): fk_1 = models.ForeignKey(SwappableModel, related_name='fk_hardref1') fk_2 = models.ForeignKey('invalid_models.SwappableModel', related_name='fk_hardref2') @@ -433,6 +453,8 @@ invalid_models.hardreferencemodel: 'fk_3' defines a relation with the model 'inv invalid_models.hardreferencemodel: 'fk_4' defines a relation with the model 'invalid_models.SwappedModel', which has been swapped out. Update the relation to point at settings.TEST_SWAPPED_MODEL. invalid_models.hardreferencemodel: 'm2m_3' defines a relation with the model 'invalid_models.SwappedModel', which has been swapped out. Update the relation to point at settings.TEST_SWAPPED_MODEL. invalid_models.hardreferencemodel: 'm2m_4' defines a relation with the model 'invalid_models.SwappedModel', which has been swapped out. Update the relation to point at settings.TEST_SWAPPED_MODEL. +invalid_models.badswappablevalue: TEST_SWAPPED_MODEL_BAD_VALUE is not of the form 'app_label.app_name'. +invalid_models.badswappablemodel: Model has been swapped out for 'not_an_app.Target' which has not been installed or is abstract. """ if not connection.features.interprets_empty_strings_as_nulls: diff --git a/tests/modeltests/invalid_models/tests.py b/tests/modeltests/invalid_models/tests.py index 787c0e5c92..6050a20880 100644 --- a/tests/modeltests/invalid_models/tests.py +++ b/tests/modeltests/invalid_models/tests.py @@ -36,7 +36,11 @@ class InvalidModelTestCase(unittest.TestCase): # set to *something* in order for the test to work. However, it's # easier to set this up as an override than to require every developer # to specify a value in their test settings. - @override_settings(TEST_SWAPPED_MODEL='invalid_models.Target') + @override_settings( + TEST_SWAPPED_MODEL='invalid_models.Target', + TEST_SWAPPED_MODEL_BAD_VALUE='not-a-model', + TEST_SWAPPED_MODEL_BAD_MODEL='not_an_app.Target', + ) def test_invalid_models(self): try: module = load_app("modeltests.invalid_models.invalid_models")