From f630373b929bc62bf4d66d60c532f7832e5fbe67 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sun, 5 Jan 2014 09:32:22 +0100 Subject: [PATCH] Fixed #21711 -- Enforced unicity of model names. --- django/apps/registry.py | 1 - django/db/models/base.py | 13 +------------ django/db/models/query_utils.py | 27 +++++++++++++++++---------- docs/releases/1.7.txt | 6 ++++++ 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/django/apps/registry.py b/django/apps/registry.py index db75f999c5..f88fb73dd6 100644 --- a/django/apps/registry.py +++ b/django/apps/registry.py @@ -185,7 +185,6 @@ class Apps(object): # call get_app_config(). model_name = model._meta.model_name app_models = self.all_models[app_label] - # Defensive check for extra safety. if model_name in app_models: raise RuntimeError( "Conflicting '%s' models in application '%s': %s and %s." % diff --git a/django/db/models/base.py b/django/db/models/base.py index 0de21fafc9..531dd8adcc 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -162,12 +162,6 @@ class ModelBase(type): new_class._default_manager = new_class._default_manager._copy_to_model(new_class) new_class._base_manager = new_class._base_manager._copy_to_model(new_class) - # Bail out early if we have already created this class. - try: - return new_class._meta.apps.get_registered_model(new_class._meta.app_label, name) - except LookupError: - pass - # Add all attributes to the class. for obj_name, obj in attrs.items(): new_class.add_to_class(obj_name, obj) @@ -285,13 +279,8 @@ class ModelBase(type): return new_class new_class._prepare() - new_class._meta.apps.register_model(new_class._meta.app_label, new_class) - # Because of the way imports happen (recursively), we may or may not be - # the first time this model tries to register with the framework. There - # should only be one class for each model, so we always return the - # registered version. - return new_class._meta.apps.get_registered_model(new_class._meta.app_label, name) + return new_class def copy_managers(cls, base_managers): # This is in-place sorting of an Options attribute, but that's fine. diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py index 2aa4856cf0..b6971e9f24 100644 --- a/django/db/models/query_utils.py +++ b/django/db/models/query_utils.py @@ -7,6 +7,7 @@ circular import difficulties. """ from __future__ import unicode_literals +from django.apps import apps from django.db.backends import utils from django.utils import six from django.utils import tree @@ -185,22 +186,28 @@ def deferred_class_factory(model, attrs): being replaced with DeferredAttribute objects. The "pk_value" ties the deferred attributes to a particular instance of the model. """ - class Meta: - proxy = True - app_label = model._meta.app_label - # The app registry wants a unique name for each model, otherwise the new - # class won't be created (we get an old one back). Therefore, we generate + # class won't be created (we get an exception). Therefore, we generate # the name using the passed in attrs. It's OK to reuse an existing class # object if the attrs are identical. name = "%s_Deferred_%s" % (model.__name__, '_'.join(sorted(list(attrs)))) name = utils.truncate_name(name, 80, 32) - overrides = dict((attr, DeferredAttribute(attr, model)) for attr in attrs) - overrides["Meta"] = Meta - overrides["__module__"] = model.__module__ - overrides["_deferred"] = True - return type(str(name), (model,), overrides) + try: + return apps.get_model(model._meta.app_label, name) + + except LookupError: + + class Meta: + proxy = True + app_label = model._meta.app_label + + overrides = dict((attr, DeferredAttribute(attr, model)) for attr in attrs) + overrides["Meta"] = Meta + overrides["__module__"] = model.__module__ + overrides["_deferred"] = True + return type(str(name), (model,), overrides) + # The above function is also used to unpickle model instances with deferred # fields. diff --git a/docs/releases/1.7.txt b/docs/releases/1.7.txt index c684454181..a4b230510a 100644 --- a/docs/releases/1.7.txt +++ b/docs/releases/1.7.txt @@ -664,6 +664,12 @@ If you have two apps with the same label, you should create an :class:`~django.apps.AppConfig.label` there. You should then adjust your code wherever it references this application or its models with the old label. +It isn't possible to import the same model twice through different paths any +more. As of Django 1.6, this may happen only if you're manually putting a +directory and a subdirectory on :envvar:`PYTHONPATH`. Refer to the section on +the new project layout in the :doc:`1.4 release notes ` for +migration instructions. + You should make sure that your project doesn't import models from applications that aren't in :setting:`INSTALLED_APPS`. Relations involving such models may not be created properly. Future versions of Django may forbid this entirely.