diff -r b758351d191f -r cc9b7e14412b web/lib/django/db/models/loading.py --- a/web/lib/django/db/models/loading.py Wed May 19 17:43:59 2010 +0200 +++ b/web/lib/django/db/models/loading.py Tue May 25 02:43:45 2010 +0200 @@ -4,7 +4,9 @@ from django.core.exceptions import ImproperlyConfigured from django.utils.datastructures import SortedDict from django.utils.importlib import import_module +from django.utils.module_loading import module_has_submodule +import imp import sys import os import threading @@ -35,6 +37,7 @@ postponed = [], nesting_level = 0, write_lock = threading.RLock(), + _get_models_cache = {}, ) def __init__(self): @@ -70,17 +73,29 @@ """ self.handled[app_name] = None self.nesting_level += 1 + app_module = import_module(app_name) try: models = import_module('.models', app_name) except ImportError: self.nesting_level -= 1 - if can_postpone: - # Either the app has no models, or the package is still being - # imported by Python and the model module isn't available yet. - # We will check again once all the recursion has finished (in - # populate). - self.postponed.append(app_name) - return None + # If the app doesn't have a models module, we can just ignore the + # ImportError and return no models for it. + if not module_has_submodule(app_module, 'models'): + return None + # But if the app does have a models module, we need to figure out + # whether to suppress or propagate the error. If can_postpone is + # True then it may be that the package is still being imported by + # Python and the models module isn't available yet. So we add the + # app to the postponed list and we'll try it again after all the + # recursion has finished (in populate). If can_postpone is False + # then it's time to raise the ImportError. + else: + if can_postpone: + self.postponed.append(app_name) + return None + else: + raise + self.nesting_level -= 1 if models not in self.app_store: self.app_store[models] = len(self.app_store) @@ -122,7 +137,7 @@ return None else: return mod - raise ImproperlyConfigured, "App with label %s could not be found" % app_label + raise ImproperlyConfigured("App with label %s could not be found" % app_label) finally: self.write_lock.release() @@ -131,19 +146,38 @@ self._populate() return self.app_errors - def get_models(self, app_mod=None): + 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. + + By default, auto-created models (i.e., m2m models without an + explicit intermediate table) are not included. However, if you + specify include_auto_created=True, they will be. + + By default, models created to satisfy deferred attribute + queries are *not* included in the list of models. However, if + you specify include_deferred, they will be. """ + cache_key = (app_mod, include_auto_created, include_deferred) + try: + return self._get_models_cache[cache_key] + except KeyError: + pass self._populate() if app_mod: - return self.app_models.get(app_mod.__name__.split('.')[-2], SortedDict()).values() + app_list = [self.app_models.get(app_mod.__name__.split('.')[-2], SortedDict())] else: - model_list = [] - for app_entry in self.app_models.itervalues(): - model_list.extend(app_entry.values()) - return model_list + app_list = self.app_models.itervalues() + 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)) + ) + self._get_models_cache[cache_key] = model_list + return model_list def get_model(self, app_label, model_name, seed_cache=True): """ @@ -177,6 +211,7 @@ if os.path.splitext(fname1)[0] == os.path.splitext(fname2)[0]: continue model_dict[model_name] = model + self._get_models_cache.clear() cache = AppCache()