Fixed #15866, #15850 -- Prevented get_model() and get_models() from returning not-installed models (by default). Thanks adsva for report.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@16053 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Carl Meyer 2011-04-20 17:58:37 +00:00
parent fda8a9a390
commit 6bdaef26ec
8 changed files with 67 additions and 12 deletions

View File

@ -90,7 +90,8 @@ class ContentType(models.Model):
def model_class(self):
"Returns the Python model class for this type of content."
from django.db import models
return models.get_model(self.app_label, self.model)
return models.get_model(self.app_label, self.model,
only_installed=False)
def get_object_for_this_type(self, **kwargs):
"""

View File

@ -88,7 +88,8 @@ class ModelBase(type):
new_class._base_manager = new_class._base_manager._copy_to_model(new_class)
# Bail out early if we have already created this class.
m = get_model(new_class._meta.app_label, name, False)
m = get_model(new_class._meta.app_label, name,
seed_cache=False, only_installed=False)
if m is not None:
return m
@ -201,7 +202,8 @@ class ModelBase(type):
# 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 get_model(new_class._meta.app_label, name, False)
return get_model(new_class._meta.app_label, name,
seed_cache=False, only_installed=False)
def copy_managers(cls, base_managers):
# This is in-place sorting of an Options attribute, but that's fine.

View File

@ -67,7 +67,8 @@ def add_lazy_relation(cls, field, relation, operation):
# string right away. If get_model returns None, it means that the related
# model isn't loaded yet, so we need to pend the relation until the class
# is prepared.
model = get_model(app_label, model_name, False)
model = get_model(app_label, model_name,
seed_cache=False, only_installed=False)
if model:
operation(field, model, cls)
else:

View File

@ -25,7 +25,11 @@ class AppCache(object):
# Keys of app_store are the model modules for each application.
app_store = SortedDict(),
# Mapping of installed app_labels to model modules for that app.
app_labels = {},
# Mapping of app_labels to a dictionary of model names to model code.
# May contain apps that are not installed.
app_models = SortedDict(),
# Mapping of app_labels to errors raised when trying to import the app.
@ -66,6 +70,13 @@ class AppCache(object):
finally:
self.write_lock.release()
def _label_for(self, app_mod):
"""
Return app_label for given models module.
"""
return app_mod.__name__.split('.')[-2]
def load_app(self, app_name, can_postpone=False):
"""
Loads the app with the provided fully qualified name, and returns the
@ -99,6 +110,7 @@ class AppCache(object):
self.nesting_level -= 1
if models not in self.app_store:
self.app_store[models] = len(self.app_store)
self.app_labels[self._label_for(models)] = models
return models
def app_cache_ready(self):
@ -146,7 +158,8 @@ class AppCache(object):
self._populate()
return self.app_errors
def get_models(self, app_mod=None, include_auto_created=False, include_deferred=False):
def get_models(self, app_mod=None,
include_auto_created=False, include_deferred=False):
"""
Given a module containing models, returns a list of the models.
Otherwise returns a list of all installed models.
@ -166,20 +179,26 @@ class AppCache(object):
pass
self._populate()
if app_mod:
app_list = [self.app_models.get(app_mod.__name__.split('.')[-2], SortedDict())]
if app_mod in self.app_store:
app_list = [self.app_models.get(self._label_for(app_mod),
SortedDict())]
else:
app_list = []
else:
app_list = self.app_models.itervalues()
app_list = [self.app_models.get(app_label, SortedDict())
for app_label in self.app_labels.iterkeys()]
model_list = []
for app in app_list:
model_list.extend(
model for model in app.values()
if ((not model._deferred or include_deferred)
and (not model._meta.auto_created or include_auto_created))
if ((not model._deferred or include_deferred) and
(not model._meta.auto_created or include_auto_created))
)
self._get_models_cache[cache_key] = model_list
return model_list
def get_model(self, app_label, model_name, seed_cache=True):
def get_model(self, app_label, model_name,
seed_cache=True, only_installed=True):
"""
Returns the model matching the given app_label and case-insensitive
model_name.
@ -188,6 +207,8 @@ class AppCache(object):
"""
if seed_cache:
self._populate()
if only_installed and app_label not in self.app_labels:
return None
return self.app_models.get(app_label, SortedDict()).get(model_name.lower())
def register_models(self, app_label, *models):

View File

@ -1023,7 +1023,11 @@ class ManageValidate(AdminScriptTestCase):
def test_app_with_import(self):
"manage.py validate does not raise errors when an app imports a base class that itself has an abstract base"
self.write_settings('settings.py',
apps=['admin_scripts.app_with_import', 'django.contrib.comments'],
apps=['admin_scripts.app_with_import',
'django.contrib.comments',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sites'],
sdict={'DEBUG': True})
args = ['validate']
out, err = self.run_manage(args)

View File

@ -0,0 +1,5 @@
from django.db import models
class NotInstalledModel(models.Model):
pass

View File

@ -4,7 +4,7 @@ import sys
import time
from django.conf import Settings
from django.db.models.loading import cache, load_app
from django.db.models.loading import cache, load_app, get_model, get_models
from django.utils.unittest import TestCase
@ -81,3 +81,24 @@ class EggLoadingTest(TestCase):
# Make sure the message is indicating the actual
# problem in the broken app.
self.assertTrue("modelz" in e.args[0])
class GetModelsTest(TestCase):
def setUp(self):
import not_installed.models
self.not_installed_module = not_installed.models
def test_get_model_only_returns_installed_models(self):
self.assertEqual(
get_model("not_installed", "NotInstalledModel"), None)
def test_get_models_only_returns_installed_models(self):
self.assertFalse(
"NotInstalledModel" in
[m.__name__ for m in get_models()])
def test_get_models_with_app_label_only_returns_installed_models(self):
self.assertEqual(get_models(self.not_installed_module), [])