[1.8.x] Fixed #24287 -- Added friendly error if a model is in a models.py outside an installed app.

This commit is contained in:
jMyles 2015-02-05 16:50:31 -05:00 committed by Tim Graham
parent 64a9540829
commit ac576e9f45
2 changed files with 50 additions and 11 deletions

View File

@ -2,34 +2,41 @@ from __future__ import unicode_literals
import copy import copy
import inspect import inspect
from itertools import chain
import sys import sys
import warnings import warnings
from itertools import chain
from django.apps import apps from django.apps import apps
from django.apps.config import MODELS_MODULE_NAME from django.apps.config import MODELS_MODULE_NAME
from django.conf import settings from django.conf import settings
from django.core import checks from django.core import checks
from django.core.exceptions import (FieldDoesNotExist, ObjectDoesNotExist, from django.core.exceptions import (
MultipleObjectsReturned, FieldError, ValidationError, NON_FIELD_ERRORS) NON_FIELD_ERRORS, FieldDoesNotExist, FieldError, ImproperlyConfigured,
from django.db import (router, connections, transaction, DatabaseError, MultipleObjectsReturned, ObjectDoesNotExist, ValidationError,
DEFAULT_DB_ALIAS, DJANGO_VERSION_PICKLE_KEY) )
from django.db import (
DEFAULT_DB_ALIAS, DJANGO_VERSION_PICKLE_KEY, DatabaseError, connections,
router, transaction,
)
from django.db.models import signals from django.db.models import signals
from django.db.models.constants import LOOKUP_SEP from django.db.models.constants import LOOKUP_SEP
from django.db.models.deletion import Collector from django.db.models.deletion import Collector
from django.db.models.fields import AutoField from django.db.models.fields import AutoField
from django.db.models.fields.related import (ForeignObjectRel, ManyToOneRel, from django.db.models.fields.related import (
OneToOneField, add_lazy_relation) ForeignObjectRel, ManyToOneRel, OneToOneField, add_lazy_relation,
)
from django.db.models.manager import ensure_default_manager from django.db.models.manager import ensure_default_manager
from django.db.models.options import Options from django.db.models.options import Options
from django.db.models.query import Q from django.db.models.query import Q
from django.db.models.query_utils import DeferredAttribute, deferred_class_factory from django.db.models.query_utils import (
DeferredAttribute, deferred_class_factory,
)
from django.utils import six from django.utils import six
from django.utils.deprecation import RemovedInDjango19Warning from django.utils.deprecation import RemovedInDjango19Warning
from django.utils.encoding import force_str, force_text from django.utils.encoding import force_str, force_text
from django.utils.functional import curry from django.utils.functional import curry
from django.utils.six.moves import zip from django.utils.six.moves import zip
from django.utils.text import get_text_list, capfirst from django.utils.text import capfirst, get_text_list
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.version import get_version from django.utils.version import get_version
@ -115,8 +122,14 @@ class ModelBase(type):
app_label_index = package_components.index(MODELS_MODULE_NAME) + 1 app_label_index = package_components.index(MODELS_MODULE_NAME) + 1
except ValueError: except ValueError:
app_label_index = 1 app_label_index = 1
kwargs = {"app_label": package_components[app_label_index]} try:
kwargs = {"app_label": package_components[app_label_index]}
except IndexError:
raise ImproperlyConfigured(
'Unable to detect the app label for model "%s." '
'Ensure that its module, "%s", is located inside an installed '
'app.' % (new_class.__name__, model_module.__name__)
)
else: else:
kwargs = {"app_label": app_config.label} kwargs = {"app_label": app_config.label}

View File

@ -1,8 +1,11 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import os import os
import sys
from django.apps import apps from django.apps import apps
from django.core.exceptions import ImproperlyConfigured
from django.db import models
from django.test import TestCase from django.test import TestCase
from django.test.utils import extend_sys_path from django.test.utils import extend_sys_path
from django.utils._os import upath from django.utils._os import upath
@ -75,3 +78,26 @@ class GetModelsTest(TestCase):
self.assertNotIn( self.assertNotIn(
"NotInstalledModel", "NotInstalledModel",
[m.__name__ for m in apps.get_models()]) [m.__name__ for m in apps.get_models()])
def test_exception_raised_if_model_declared_outside_app(self):
class FakeModule(models.Model):
__name__ = str("models_that_do_not_live_in_an_app")
sys.modules['models_not_in_app'] = FakeModule
def declare_model_outside_app():
models.base.ModelBase.__new__(
models.base.ModelBase,
str('Outsider'),
(models.Model,),
{'__module__': 'models_not_in_app'})
msg = (
'Unable to detect the app label for model "Outsider." '
'Ensure that its module, "models_that_do_not_live_in_an_app", '
'is located inside an installed app.'
)
with self.assertRaisesMessage(ImproperlyConfigured, msg):
declare_model_outside_app()