web/lib/django/db/models/loading.py
changeset 29 cc9b7e14412b
parent 0 0d40e90630ef
--- 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()