Fixed #21711 -- Enforced unicity of model names.

This commit is contained in:
Aymeric Augustin 2014-01-05 09:32:22 +01:00
parent f5f7617167
commit f630373b92
4 changed files with 24 additions and 23 deletions

View File

@ -185,7 +185,6 @@ class Apps(object):
# call get_app_config(). # call get_app_config().
model_name = model._meta.model_name model_name = model._meta.model_name
app_models = self.all_models[app_label] app_models = self.all_models[app_label]
# Defensive check for extra safety.
if model_name in app_models: if model_name in app_models:
raise RuntimeError( raise RuntimeError(
"Conflicting '%s' models in application '%s': %s and %s." % "Conflicting '%s' models in application '%s': %s and %s." %

View File

@ -162,12 +162,6 @@ class ModelBase(type):
new_class._default_manager = new_class._default_manager._copy_to_model(new_class) 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) 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. # Add all attributes to the class.
for obj_name, obj in attrs.items(): for obj_name, obj in attrs.items():
new_class.add_to_class(obj_name, obj) new_class.add_to_class(obj_name, obj)
@ -285,13 +279,8 @@ class ModelBase(type):
return new_class return new_class
new_class._prepare() new_class._prepare()
new_class._meta.apps.register_model(new_class._meta.app_label, new_class) 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 return new_class
# 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)
def copy_managers(cls, base_managers): def copy_managers(cls, base_managers):
# This is in-place sorting of an Options attribute, but that's fine. # This is in-place sorting of an Options attribute, but that's fine.

View File

@ -7,6 +7,7 @@ circular import difficulties.
""" """
from __future__ import unicode_literals from __future__ import unicode_literals
from django.apps import apps
from django.db.backends import utils from django.db.backends import utils
from django.utils import six from django.utils import six
from django.utils import tree from django.utils import tree
@ -185,23 +186,29 @@ def deferred_class_factory(model, attrs):
being replaced with DeferredAttribute objects. The "pk_value" ties the being replaced with DeferredAttribute objects. The "pk_value" ties the
deferred attributes to a particular instance of the model. 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 # 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 # the name using the passed in attrs. It's OK to reuse an existing class
# object if the attrs are identical. # object if the attrs are identical.
name = "%s_Deferred_%s" % (model.__name__, '_'.join(sorted(list(attrs)))) name = "%s_Deferred_%s" % (model.__name__, '_'.join(sorted(list(attrs))))
name = utils.truncate_name(name, 80, 32) name = utils.truncate_name(name, 80, 32)
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 = dict((attr, DeferredAttribute(attr, model)) for attr in attrs)
overrides["Meta"] = Meta overrides["Meta"] = Meta
overrides["__module__"] = model.__module__ overrides["__module__"] = model.__module__
overrides["_deferred"] = True overrides["_deferred"] = True
return type(str(name), (model,), overrides) return type(str(name), (model,), overrides)
# The above function is also used to unpickle model instances with deferred # The above function is also used to unpickle model instances with deferred
# fields. # fields.
deferred_class_factory.__safe_for_unpickling__ = True deferred_class_factory.__safe_for_unpickling__ = True

View File

@ -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 :class:`~django.apps.AppConfig.label` there. You should then adjust your code
wherever it references this application or its models with the old label. 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 </releases/1.4>` for
migration instructions.
You should make sure that your project doesn't import models from applications 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 that aren't in :setting:`INSTALLED_APPS`. Relations involving such models may
not be created properly. Future versions of Django may forbid this entirely. not be created properly. Future versions of Django may forbid this entirely.