Skip to content

Commit

Permalink
Fixed #21711 -- Enforced unicity of model names.
Browse files Browse the repository at this point in the history
  • Loading branch information
aaugustin committed Jan 5, 2014
1 parent f5f7617 commit f630373
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 23 deletions.
1 change: 0 additions & 1 deletion django/apps/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,6 @@ def register_model(self, app_label, model):
# 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." %
Expand Down
13 changes: 1 addition & 12 deletions django/db/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,6 @@ def __new__(cls, name, bases, attrs):
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)
Expand Down Expand Up @@ -285,13 +279,8 @@ def __new__(cls, name, bases, attrs):
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.
Expand Down
27 changes: 17 additions & 10 deletions django/db/models/query_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"""
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
Expand Down Expand Up @@ -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.
Expand Down
6 changes: 6 additions & 0 deletions docs/releases/1.7.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 </releases/1.4>` 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.
Expand Down

0 comments on commit f630373

Please sign in to comment.