Index: django/test/simple.py
===================================================================
--- django/test/simple.py	(revision 10759)
+++ django/test/simple.py	(working copy)
@@ -176,11 +176,15 @@
                 suite.addTest(build_test(label))
             else:
                 app = get_app(label)
-                suite.addTest(build_suite(app))
+                mod = app.models_module
+                if mod:
+                  suite.addTest(build_suite(mod))
     else:
         for app in get_apps():
-            suite.addTest(build_suite(app))
-
+            mod = app.models_module
+            if mod:
+                suite.addTest(build_suite(mod))
+    
     for test in extra_tests:
         suite.addTest(test)
 
Index: django/test/client.py
===================================================================
--- django/test/client.py	(revision 10759)
+++ django/test/client.py	(working copy)
@@ -8,7 +8,7 @@
 except ImportError:
     from StringIO import StringIO
 
-from django.conf import settings
+from django.conf import settings, get_installed_app_paths
 from django.contrib.auth import authenticate, login
 from django.core.handlers.base import BaseHandler
 from django.core.handlers.wsgi import WSGIRequest
@@ -177,7 +177,7 @@
         """
         Obtains the current session variables.
         """
-        if 'django.contrib.sessions' in settings.INSTALLED_APPS:
+        if 'django.contrib.sessions' in get_installed_app_paths():
             engine = import_module(settings.SESSION_ENGINE)
             cookie = self.cookies.get(settings.SESSION_COOKIE_NAME, None)
             if cookie:
@@ -406,7 +406,7 @@
         """
         user = authenticate(**credentials)
         if user and user.is_active \
-                and 'django.contrib.sessions' in settings.INSTALLED_APPS:
+                and 'django.contrib.sessions' in get_installed_app_paths():
             engine = import_module(settings.SESSION_ENGINE)
 
             # Create a fake request to store login details.
Index: django/db/models/base.py
===================================================================
--- django/db/models/base.py	(revision 10759)
+++ django/db/models/base.py	(working copy)
@@ -17,7 +17,7 @@
 from django.db.models.options import Options
 from django.db import connection, transaction, DatabaseError
 from django.db.models import signals
-from django.db.models.loading import register_models, get_model
+from django.db.models.loading import register_models, get_model, get_app_label
 from django.utils.functional import curry
 from django.utils.encoding import smart_str, force_unicode, smart_unicode
 from django.conf import settings
@@ -82,6 +82,10 @@
                 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)
 
+        if getattr(new_class._meta, 'app_label', None) is None:
+            # Figure out the app_label.
+            new_class._meta.app_label = get_app_label(new_class)
+
         # Bail out early if we have already created this class.
         m = get_model(new_class._meta.app_label, name, False)
         if m is not None:
Index: django/db/models/options.py
===================================================================
--- django/db/models/options.py	(revision 10759)
+++ django/db/models/options.py	(working copy)
@@ -5,7 +5,7 @@
 except NameError:
     from sets import Set as set     # Python 2.3 fallback
 
-from django.conf import settings
+from django.conf import settings, get_installed_app_paths
 from django.db.models.related import RelatedObject
 from django.db.models.fields.related import ManyToManyRel
 from django.db.models.fields import AutoField, FieldDoesNotExist
@@ -58,7 +58,7 @@
         from django.db.backends.util import truncate_name
 
         cls._meta = self
-        self.installed = re.sub('\.models$', '', cls.__module__) in settings.INSTALLED_APPS
+        self.installed = re.sub('\.models$', '', cls.__module__) in get_installed_app_paths()
         # First, construct the default values for these options.
         self.object_name = cls.__name__
         self.module_name = self.object_name.lower()
Index: django/db/models/loading.py
===================================================================
--- django/db/models/loading.py	(revision 10759)
+++ django/db/models/loading.py	(working copy)
@@ -1,16 +1,16 @@
 "Utilities for loading models and the modules that contain them."
 
-from django.conf import settings
+from django.conf import settings, app
 from django.core.exceptions import ImproperlyConfigured
 from django.utils.datastructures import SortedDict
 from django.utils.importlib import import_module
 
 import sys
-import os
+import os, os.path
 import threading
 
 __all__ = ('get_apps', 'get_app', 'get_models', 'get_model', 'register_models',
-        'load_app', 'app_cache_ready')
+        'load_app', 'app_cache_ready', 'find_app')
 
 class AppCache(object):
     """
@@ -29,6 +29,16 @@
         # Mapping of app_labels to errors raised when trying to import the app.
         app_errors = {},
 
+
+        # Mapping of app_labels to app instances.
+        app_map = {},
+
+        # Mapping of app module names to app instances.
+        mod_map = {},
+
+        # List of app instances.
+        app_instances = [],
+
         # -- Everything below here is only used when populating the cache --
         loaded = False,
         handled = {},
@@ -52,13 +62,42 @@
         try:
             if self.loaded:
                 return
-            for app_name in settings.INSTALLED_APPS:
+            # We first loop through, setting up the app instances and
+            # the relevant maps so that we can find the instances by app_label
+            # or app module name. These need to be ready because we need to be
+            # able to get the app_label e.g. for applying to model classes.
+            # If a model class is loaded indirectly without being in an
+            # installed app, the app_label used will be what it is now - the
+            # last part of the app module name.
+            # Note that app_map and mod_map will contain entries even
+            # when an app cannot be loaded by load_app.
+            for app_entry in settings.INSTALLED_APPS:
+                if isinstance(app_entry, basestring):
+                    the_app = app(app_entry)
+                else:
+                    the_app = app_entry
+                self.app_map[the_app.label] = the_app
+                self.mod_map[the_app.path] = the_app
+
+            for app_entry in settings.INSTALLED_APPS:
+                if isinstance(app_entry, basestring):
+                    the_app = self.mod_map[app_entry]
+                else:
+                    the_app = app_entry
+                app_name = the_app.path
                 if app_name in self.handled:
                     continue
-                self.load_app(app_name, True)
+                try:
+                    self.load_app(app_name, True)
+                    self.app_instances.append(the_app)
+                except Exception, e:
+                    # Problem importing the app
+                    self.app_errors[app_name] = e
             if not self.nesting_level:
                 for app_name in self.postponed:
+                    the_app = self.mod_map[app_name]
                     self.load_app(app_name)
+                    self.app_instances.append(the_app)                    
                 self.loaded = True
         finally:
             self.write_lock.release()
@@ -70,8 +109,20 @@
         """
         self.handled[app_name] = None
         self.nesting_level += 1
+        mod = import_module(app_name)
+        if app_name in self.mod_map:
+            the_app = self.mod_map[app_name]
+        else:
+            # An app can be loaded by load_app even before get_apps is
+            # called. In this case, we just make a new app and add it to the maps.
+            the_app = app(app_name)
+            self.app_map[the_app.label] = the_app
+            self.mod_map[app_name] = the_app
+            self.app_instances.append(the_app)
+        the_app.app_module = mod
         try:
             models = import_module('.models', app_name)
+            the_app.models_module = models
         except ImportError:
             self.nesting_level -= 1
             if can_postpone:
@@ -104,45 +155,61 @@
         # list page, for example.
         apps = [(v, k) for k, v in self.app_store.items()]
         apps.sort()
-        return [elt[1] for elt in apps]
+        #return [elt[1] for elt in apps]
+        return self.app_instances
 
+    def get_app_label(self, model_class):
+        "Returns the app label to be used for a model class."
+        key = model_class.__module__
+        i = key.rfind('.')
+        assert i > 0, "Model class must be defined in a package sub-module"
+        key = key[:i]
+        if key in self.mod_map:
+            rv = self.mod_map[key].label
+        else:
+            i = key.rfind('.')
+            if i < 0:
+                rv = key
+            else:
+                rv = key[i + 1:]
+        return rv
+    
+    def find_app(self, app_path):
+        "Find an app instance, given the application path."
+        self._populate()
+        return self.mod_map[app_path]
+
     def get_app(self, app_label, emptyOK=False):
         """
-        Returns the module containing the models for the given app_label. If
+        Returns the app instance for the given app_label. If
         the app has no models in it and 'emptyOK' is True, returns None.
         """
         self._populate()
-        self.write_lock.acquire()
-        try:
-            for app_name in settings.INSTALLED_APPS:
-                if app_label == app_name.split('.')[-1]:
-                    mod = self.load_app(app_name, False)
-                    if mod is None:
-                        if emptyOK:
-                            return None
-                    else:
-                        return mod
-            raise ImproperlyConfigured, "App with label %s could not be found" % app_label
-        finally:
-            self.write_lock.release()
+        if app_label not in self.app_map:
+            rv = None
+        else:
+            rv = self.app_map[app_label]
+        if emptyOK or rv is not None:
+            return rv
+        raise ImproperlyConfigured, "App with label %s could not be found" % app_label
 
     def get_app_errors(self):
         "Returns the map of known problems with the INSTALLED_APPS."
         self._populate()
         return self.app_errors
 
-    def get_models(self, app_mod=None):
+    def get_models(self, the_app=None):
         """
-        Given a module containing models, returns a list of the models.
+        Given an app instance, returns a list of its models.
         Otherwise returns a list of all installed models.
         """
         self._populate()
-        if app_mod:
-            return self.app_models.get(app_mod.__name__.split('.')[-2], SortedDict()).values()
+        if the_app:
+            return self.app_models.get(the_app.label, SortedDict()).values()
         else:
             model_list = []
-            for app_entry in self.app_models.itervalues():
-                model_list.extend(app_entry.values())
+            for the_app in self.app_instances:
+                model_list.extend(get_models(the_app))
             return model_list
 
     def get_model(self, app_label, model_name, seed_cache=True):
@@ -190,3 +257,5 @@
 register_models = cache.register_models
 load_app = cache.load_app
 app_cache_ready = cache.app_cache_ready
+find_app = cache.find_app
+get_app_label = cache.get_app_label
Index: django/db/models/__init__.py
===================================================================
--- django/db/models/__init__.py	(revision 10759)
+++ django/db/models/__init__.py	(working copy)
@@ -1,7 +1,7 @@
 from django.conf import settings
 from django.core.exceptions import ObjectDoesNotExist, ImproperlyConfigured
 from django.db import connection
-from django.db.models.loading import get_apps, get_app, get_models, get_model, register_models
+from django.db.models.loading import get_apps, get_app, get_models, get_model, register_models, find_app
 from django.db.models.query import Q
 from django.db.models.expressions import F
 from django.db.models.manager import Manager
Index: django/conf/__init__.py
===================================================================
--- django/conf/__init__.py	(revision 10759)
+++ django/conf/__init__.py	(working copy)
@@ -89,7 +89,9 @@
         # of all those apps.
         new_installed_apps = []
         for app in self.INSTALLED_APPS:
-            if app.endswith('.*'):
+            if not isinstance(app, basestring) or not app.endswith('.*'):
+                new_installed_apps.append(app)
+            else:
                 app_mod = importlib.import_module(app[:-2])
                 appdir = os.path.dirname(app_mod.__file__)
                 app_subdirs = os.listdir(appdir)
@@ -98,8 +100,6 @@
                 for d in app_subdirs:
                     if name_pattern.match(d) and os.path.isdir(os.path.join(appdir, d)):
                         new_installed_apps.append('%s.%s' % (app[:-2], d))
-            else:
-                new_installed_apps.append(app)
         self.INSTALLED_APPS = new_installed_apps
 
         if hasattr(time, 'tzset'):
@@ -134,3 +134,25 @@
 
 settings = LazySettings()
 
+class app(object):
+    """Configuration directive for specifying an app."""
+    def __init__(self, path, app_label=None, verbose_name=None):
+        self.path = path
+        # if name isn't specified, get the last part of the Python dotted path
+        self.label = app_label or path.split('.')[-1]
+        self.verbose_name = verbose_name or self.label
+        self.app_module = None    # will be filled in by loading.py
+        self.models_module = None # will be filled in by loading.py
+
+    def __repr__(self):
+        return "<app: %s>" % self.path
+
+def get_installed_app_paths():
+    "Return the paths of all entries in settings.INSTALLED_APPS."
+    rv = []
+    for a in settings.INSTALLED_APPS:
+        if isinstance(a, basestring):
+            rv.append(a)
+        else:
+            rv.append(a.path)
+    return rv
Index: django/core/management/commands/loaddata.py
===================================================================
--- django/core/management/commands/loaddata.py	(revision 10759)
+++ django/core/management/commands/loaddata.py	(working copy)
@@ -76,7 +76,7 @@
         if has_bz2:
             compression_types['bz2'] = bz2.BZ2File
 
-        app_fixtures = [os.path.join(os.path.dirname(app.__file__), 'fixtures') for app in get_apps()]
+        app_fixtures = [os.path.join(os.path.dirname(app.app_module.__file__), 'fixtures') for app in get_apps() if app.app_module]
         for fixture_label in fixture_labels:
             parts = fixture_label.split('.')
 
Index: django/core/management/commands/flush.py
===================================================================
--- django/core/management/commands/flush.py	(revision 10759)
+++ django/core/management/commands/flush.py	(working copy)
@@ -1,3 +1,4 @@
+from django.conf import get_installed_app_paths
 from django.core.management.base import NoArgsCommand, CommandError
 from django.core.management.color import no_style
 from django.utils.importlib import import_module
@@ -22,7 +23,7 @@
 
         # Import the 'management' module within each installed app, to register
         # dispatcher events.
-        for app_name in settings.INSTALLED_APPS:
+        for app_name in get_installed_app_paths():
             try:
                 import_module('.management', app_name)
             except ImportError:
Index: django/core/management/commands/syncdb.py
===================================================================
--- django/core/management/commands/syncdb.py	(revision 10759)
+++ django/core/management/commands/syncdb.py	(working copy)
@@ -1,3 +1,4 @@
+from django.conf import get_installed_app_paths
 from django.core.management.base import NoArgsCommand
 from django.core.management.color import no_style
 from django.utils.importlib import import_module
@@ -29,7 +30,7 @@
 
         # Import the 'management' module within each installed app, to register
         # dispatcher events.
-        for app_name in settings.INSTALLED_APPS:
+        for app_name in get_installed_app_paths():
             try:
                 import_module('.management', app_name)
             except ImportError, exc:
@@ -56,7 +57,7 @@
 
         # Create the tables for each model
         for app in models.get_apps():
-            app_name = app.__name__.split('.')[-2]
+            app_name = app.label
             model_list = models.get_models(app)
             for model in model_list:
                 # Create the model's database table, if it doesn't already exist.
@@ -81,7 +82,7 @@
         # Create the m2m tables. This must be done after all tables have been created
         # to ensure that all referred tables will exist.
         for app in models.get_apps():
-            app_name = app.__name__.split('.')[-2]
+            app_name = app.label
             model_list = models.get_models(app)
             for model in model_list:
                 if model in created_models:
@@ -104,7 +105,7 @@
         # Install custom SQL for the app (but only if this
         # is a model we've just created)
         for app in models.get_apps():
-            app_name = app.__name__.split('.')[-2]
+            app_name = app.label
             for model in models.get_models(app):
                 if model in created_models:
                     custom_sql = custom_sql_for_model(model, self.style)
@@ -128,7 +129,7 @@
                             print "No custom SQL for %s.%s model" % (app_name, model._meta.object_name)
         # Install SQL indicies for all newly created models
         for app in models.get_apps():
-            app_name = app.__name__.split('.')[-2]
+            app_name = app.label
             for model in models.get_models(app):
                 if model in created_models:
                     index_sql = connection.creation.sql_indexes_for_model(model, self.style)
Index: django/core/management/__init__.py
===================================================================
--- django/core/management/__init__.py	(revision 10759)
+++ django/core/management/__init__.py	(working copy)
@@ -97,8 +97,8 @@
 
         # Find the installed apps
         try:
-            from django.conf import settings
-            apps = settings.INSTALLED_APPS
+            from django.conf import settings, get_installed_app_paths
+            apps = get_installed_app_paths()
         except (AttributeError, EnvironmentError, ImportError):
             apps = []
 
Index: django/core/management/sql.py
===================================================================
--- django/core/management/sql.py	(revision 10759)
+++ django/core/management/sql.py	(working copy)
@@ -137,7 +137,7 @@
     output = []
 
     app_models = get_models(app)
-    app_dir = os.path.normpath(os.path.join(os.path.dirname(app.__file__), 'sql'))
+    app_dir = os.path.normpath(os.path.join(os.path.dirname(app.app_module.__file__), 'sql'))
 
     for model in app_models:
         output.extend(custom_sql_for_model(model, style))
@@ -161,7 +161,7 @@
     from django.conf import settings
 
     opts = model._meta
-    app_dir = os.path.normpath(os.path.join(os.path.dirname(models.get_app(model._meta.app_label).__file__), 'sql'))
+    app_dir = os.path.normpath(os.path.join(os.path.dirname(models.get_app(model._meta.app_label).app_module.__file__), 'sql'))
     output = []
 
     # Post-creation SQL should come before any initial SQL data is loaded.
@@ -197,7 +197,7 @@
     from django.dispatch import dispatcher
     # Emit the post_sync signal for every application.
     for app in models.get_apps():
-        app_name = app.__name__.split('.')[-2]
+        app_name = app.label
         if verbosity >= 2:
             print "Running post-sync handlers for application", app_name
         models.signals.post_syncdb.send(sender=app, app=app,
Index: django/templatetags/__init__.py
===================================================================
--- django/templatetags/__init__.py	(revision 10759)
+++ django/templatetags/__init__.py	(working copy)
@@ -1,7 +1,7 @@
-from django.conf import settings
+from django.conf import settings, get_installed_app_paths
 from django.utils import importlib
 
-for a in settings.INSTALLED_APPS:
+for a in get_installed_app_paths():
     try:
         __path__.extend(importlib.import_module('.templatetags', a).__path__)
     except ImportError:
Index: django/views/i18n.py
===================================================================
--- django/views/i18n.py	(revision 10759)
+++ django/views/i18n.py	(working copy)
@@ -1,5 +1,5 @@
 from django import http
-from django.conf import settings
+from django.conf import settings, get_installed_app_paths
 from django.utils import importlib
 from django.utils.translation import check_for_language, activate, to_locale, get_language
 from django.utils.text import javascript_quote
@@ -122,7 +122,7 @@
         packages = ['django.conf']
     if type(packages) in (str, unicode):
         packages = packages.split('+')
-    packages = [p for p in packages if p == 'django.conf' or p in settings.INSTALLED_APPS]
+    packages = [p for p in packages if p == 'django.conf' or p in get_installed_app_paths()]
     default_locale = to_locale(settings.LANGUAGE_CODE)
     locale = to_locale(get_language())
     t = {}
Index: django/contrib/comments/__init__.py
===================================================================
--- django/contrib/comments/__init__.py	(revision 10759)
+++ django/contrib/comments/__init__.py	(working copy)
@@ -1,4 +1,4 @@
-from django.conf import settings
+from django.conf import settings, get_installed_app_paths
 from django.core import urlresolvers
 from django.core.exceptions import ImproperlyConfigured
 from django.contrib.comments.models import Comment
@@ -13,7 +13,7 @@
     """
     # Make sure the app's in INSTALLED_APPS
     comments_app = get_comment_app_name()
-    if comments_app not in settings.INSTALLED_APPS:
+    if comments_app not in get_installed_app_paths():
         raise ImproperlyConfigured("The COMMENTS_APP (%r) "\
                                    "must be in INSTALLED_APPS" % settings.COMMENTS_APP)
 
Index: django/contrib/sites/management.py
===================================================================
--- django/contrib/sites/management.py	(revision 10759)
+++ django/contrib/sites/management.py	(working copy)
@@ -2,9 +2,8 @@
 Creates the default Site object.
 """
 
-from django.db.models import signals
+from django.db.models import signals, find_app
 from django.contrib.sites.models import Site
-from django.contrib.sites import models as site_app
 
 def create_default_site(app, created_models, verbosity, **kwargs):
     if Site in created_models:
@@ -14,4 +13,4 @@
         s.save()
     Site.objects.clear_cache()
 
-signals.post_syncdb.connect(create_default_site, sender=site_app)
+signals.post_syncdb.connect(create_default_site, sender=find_app('django.contrib.sites'))
Index: django/contrib/admin/options.py
===================================================================
--- django/contrib/admin/options.py	(revision 10759)
+++ django/contrib/admin/options.py	(working copy)
@@ -8,6 +8,7 @@
 from django.contrib.admin.util import unquote, flatten_fieldsets, get_deleted_objects, model_ngettext, model_format_dict
 from django.core.exceptions import PermissionDenied
 from django.db import models, transaction
+from django.db.models import get_app
 from django.db.models.fields import BLANK_CHOICE_DASH
 from django.http import Http404, HttpResponse, HttpResponseRedirect
 from django.shortcuts import get_object_or_404, render_to_response
@@ -777,7 +778,7 @@
             'inline_admin_formsets': inline_admin_formsets,
             'errors': helpers.AdminErrorList(form, formsets),
             'root_path': self.admin_site.root_path,
-            'app_label': opts.app_label,
+            'app_label': get_app(opts.app_label).verbose_name,
         }
         context.update(extra_context or {})
         return self.render_change_form(request, context, form_url=form_url, add=True)
@@ -866,7 +867,7 @@
             'inline_admin_formsets': inline_admin_formsets,
             'errors': helpers.AdminErrorList(form, formsets),
             'root_path': self.admin_site.root_path,
-            'app_label': opts.app_label,
+            'app_label': get_app(opts.app_label).verbose_name,
         }
         context.update(extra_context or {})
         return self.render_change_form(request, context, change=True, obj=obj)
@@ -971,7 +972,7 @@
             'media': media,
             'has_add_permission': self.has_add_permission(request),
             'root_path': self.admin_site.root_path,
-            'app_label': app_label,
+            'app_label': get_app(app_label).verbose_name,
             'action_form': action_form,
             'actions_on_top': self.actions_on_top,
             'actions_on_bottom': self.actions_on_bottom,
@@ -1029,7 +1030,7 @@
             "perms_lacking": perms_needed,
             "opts": opts,
             "root_path": self.admin_site.root_path,
-            "app_label": app_label,
+            "app_label": get_app(app_label).verbose_name,
         }
         context.update(extra_context or {})
         return render_to_response(self.delete_confirmation_template or [
@@ -1056,7 +1057,7 @@
             'module_name': capfirst(force_unicode(opts.verbose_name_plural)),
             'object': obj,
             'root_path': self.admin_site.root_path,
-            'app_label': app_label,
+            'app_label': get_app(app_label).verbose_name,
         }
         context.update(extra_context or {})
         return render_to_response(self.object_history_template or [
Index: django/contrib/admin/__init__.py
===================================================================
--- django/contrib/admin/__init__.py	(revision 10759)
+++ django/contrib/admin/__init__.py	(working copy)
@@ -25,9 +25,9 @@
     LOADING = True
 
     import imp
-    from django.conf import settings
+    from django.conf import settings, get_installed_app_paths
 
-    for app in settings.INSTALLED_APPS:
+    for app in get_installed_app_paths():
         # For each app, we need to look for an admin.py inside that app's
         # package. We can't use os.path here -- recall that modules may be
         # imported different ways (think zip files) -- so we need to get
Index: django/contrib/admin/sites.py
===================================================================
--- django/contrib/admin/sites.py	(revision 10759)
+++ django/contrib/admin/sites.py	(working copy)
@@ -3,6 +3,7 @@
 from django.contrib.admin import ModelAdmin
 from django.contrib.admin import actions
 from django.contrib.auth import authenticate, login
+from django.db.models import get_app
 from django.db.models.base import ModelBase
 from django.core.exceptions import ImproperlyConfigured
 from django.shortcuts import render_to_response
@@ -342,7 +343,7 @@
                         app_dict[app_label]['models'].append(model_dict)
                     else:
                         app_dict[app_label] = {
-                            'name': app_label.title(),
+                            'name': get_app(app_label).verbose_name,
                             'app_url': app_label + '/',
                             'has_module_perms': has_module_perms,
                             'models': [model_dict],
@@ -404,7 +405,7 @@
                             # something to display, add in the necessary meta
                             # information.
                             app_dict = {
-                                'name': app_label.title(),
+                                'name': get_app(app_label).verbose_name,
                                 'app_url': '',
                                 'has_module_perms': has_module_perms,
                                 'models': [model_dict],
@@ -414,7 +415,7 @@
         # Sort the models alphabetically within each app.
         app_dict['models'].sort(lambda x, y: cmp(x['name'], y['name']))
         context = {
-            'title': _('%s administration') % capfirst(app_label),
+            'title': _('%s administration') % get_app(app_label).verbose_name,
             'app_list': [app_dict],
             'root_path': self.root_path,
         }
Index: django/contrib/contenttypes/management.py
===================================================================
--- django/contrib/contenttypes/management.py	(revision 10759)
+++ django/contrib/contenttypes/management.py	(working copy)
@@ -8,7 +8,7 @@
     entries that no longer have a matching model class.
     """
     ContentType.objects.clear_cache()
-    content_types = list(ContentType.objects.filter(app_label=app.__name__.split('.')[-2]))
+    content_types = list(ContentType.objects.filter(app_label=app.label))
     app_models = get_models(app)
     if not app_models:
         return
Index: django/contrib/auth/management/__init__.py
===================================================================
--- django/contrib/auth/management/__init__.py	(revision 10759)
+++ django/contrib/auth/management/__init__.py	(working copy)
@@ -2,8 +2,7 @@
 Creates permissions for all installed apps that need permissions.
 """
 
-from django.db.models import get_models, signals
-from django.contrib.auth import models as auth_app
+from django.db.models import get_models, signals, find_app
 
 def _get_permission_codename(action, opts):
     return u'%s_%s' % (action, opts.object_name.lower())
@@ -47,4 +46,5 @@
 signals.post_syncdb.connect(create_permissions,
     dispatch_uid = "django.contrib.auth.management.create_permissions")
 signals.post_syncdb.connect(create_superuser,
-    sender=auth_app, dispatch_uid = "django.contrib.auth.management.create_superuser")
+    sender=find_app('django.contrib.auth'),
+    dispatch_uid = "django.contrib.auth.management.create_superuser")
Index: django/utils/translation/trans_real.py
===================================================================
--- django/utils/translation/trans_real.py	(revision 10759)
+++ django/utils/translation/trans_real.py	(working copy)
@@ -115,7 +115,7 @@
     if t is not None:
         return t
 
-    from django.conf import settings
+    from django.conf import settings, get_installed_app_paths
 
     # set up the right translation class
     klass = DjangoTranslation
@@ -176,7 +176,7 @@
         if projectpath and os.path.isdir(projectpath):
             res = _merge(projectpath)
 
-        for appname in settings.INSTALLED_APPS:
+        for appname in get_installed_app_paths():
             app = import_module(appname)
             apppath = os.path.join(os.path.dirname(app.__file__), 'locale')
 
Index: django/template/loaders/app_directories.py
===================================================================
--- django/template/loaders/app_directories.py	(revision 10759)
+++ django/template/loaders/app_directories.py	(working copy)
@@ -6,7 +6,7 @@
 import os
 import sys
 
-from django.conf import settings
+from django.conf import settings, get_installed_app_paths
 from django.core.exceptions import ImproperlyConfigured
 from django.template import TemplateDoesNotExist
 from django.utils._os import safe_join
@@ -15,7 +15,7 @@
 # At compile time, cache the directories to search.
 fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
 app_template_dirs = []
-for app in settings.INSTALLED_APPS:
+for app in get_installed_app_paths():
     try:
         mod = import_module(app)
     except ImportError, e:
Index: django/template/loaders/eggs.py
===================================================================
--- django/template/loaders/eggs.py	(revision 10759)
+++ django/template/loaders/eggs.py	(working copy)
@@ -6,7 +6,7 @@
     resource_string = None
 
 from django.template import TemplateDoesNotExist
-from django.conf import settings
+from django.conf import settings, get_installed_app_paths
 
 def load_template_source(template_name, template_dirs=None):
     """
@@ -16,7 +16,7 @@
     """
     if resource_string is not None:
         pkg_name = 'templates/' + template_name
-        for app in settings.INSTALLED_APPS:
+        for app in get_installed_app_paths():
             try:
                 return (resource_string(app, pkg_name).decode(settings.FILE_CHARSET), 'egg:%s:%s' % (app, pkg_name))
             except:
Index: tests/regressiontests/admin_scripts/tests.py
===================================================================
--- tests/regressiontests/admin_scripts/tests.py	(revision 10759)
+++ tests/regressiontests/admin_scripts/tests.py	(working copy)
@@ -1041,9 +1041,9 @@
         args = ['app_command', 'auth']
         out, err = self.run_manage(args)
         self.assertNoOutput(err)
-        self.assertOutput(out, "EXECUTE:AppCommand app=<module 'django.contrib.auth.models'")
-        self.assertOutput(out, os.sep.join(['django','contrib','auth','models.py']))
-        self.assertOutput(out, "'>, options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
+        self.assertOutput(out, "EXECUTE:AppCommand app=<app: django.contrib.auth>")
+        #self.assertOutput(out, os.sep.join(['django','contrib','auth','models.py']))
+        self.assertOutput(out, ">, options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
 
     def test_app_command_no_apps(self):
         "User AppCommands raise an error when no app name is provided"
@@ -1056,12 +1056,12 @@
         args = ['app_command','auth','contenttypes']
         out, err = self.run_manage(args)
         self.assertNoOutput(err)
-        self.assertOutput(out, "EXECUTE:AppCommand app=<module 'django.contrib.auth.models'")
-        self.assertOutput(out, os.sep.join(['django','contrib','auth','models.py']))
-        self.assertOutput(out, "'>, options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
-        self.assertOutput(out, "EXECUTE:AppCommand app=<module 'django.contrib.contenttypes.models'")
-        self.assertOutput(out, os.sep.join(['django','contrib','contenttypes','models.py']))
-        self.assertOutput(out, "'>, options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
+        self.assertOutput(out, "EXECUTE:AppCommand app=<app: django.contrib.auth>")
+        #self.assertOutput(out, os.sep.join(['django','contrib','auth','models.py']))
+        self.assertOutput(out, ">, options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
+        self.assertOutput(out, "EXECUTE:AppCommand app=<app: django.contrib.contenttypes>")
+        #self.assertOutput(out, os.sep.join(['django','contrib','contenttypes','models.py']))
+        self.assertOutput(out, ">, options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
 
     def test_app_command_invalid_appname(self):
         "User AppCommands can execute when a single app name is provided"
Index: tests/runtests.py
===================================================================
--- tests/runtests.py	(revision 10759)
+++ tests/runtests.py	(working copy)
@@ -2,6 +2,7 @@
 
 import os, sys, traceback
 import unittest
+from django.conf import app, get_installed_app_paths
 
 import django.contrib as contrib
 
@@ -23,7 +24,8 @@
 
 ALWAYS_INSTALLED_APPS = [
     'django.contrib.contenttypes',
-    'django.contrib.auth',
+    #We need to use the same app label, otherwise fixtures will break...
+    app('django.contrib.auth', 'auth', 'Authentication'),
     'django.contrib.sites',
     'django.contrib.flatpages',
     'django.contrib.redirects',
@@ -58,13 +60,14 @@
 
     def runTest(self):
         from django.core.management.validation import get_validation_errors
-        from django.db.models.loading import load_app
+        from django.db.models.loading import load_app, find_app
         from cStringIO import StringIO
 
         try:
             module = load_app(self.model_label)
+            app = find_app(self.model_label)
         except Exception, e:
-            self.fail('Unable to load invalid model module')
+            self.fail('Unable to load invalid application %s: %s' % (self.model_label, e))
 
         # Make sure sys.stdout is not a tty so that we get errors without
         # coloring attached (makes matching the results easier). We restore
@@ -72,7 +75,7 @@
         orig_stdout = sys.stdout
         s = StringIO()
         sys.stdout = s
-        count = get_validation_errors(s, module)
+        count = get_validation_errors(s, app)
         sys.stdout = orig_stdout
         s.seek(0)
         error_log = s.read()
@@ -133,7 +136,7 @@
                     print "Importing model %s" % model_name
                 mod = load_app(model_label)
                 if mod:
-                    if model_label not in settings.INSTALLED_APPS:
+                    if model_label not in get_installed_app_paths():
                         settings.INSTALLED_APPS.append(model_label)
         except Exception, e:
             sys.stderr.write("Error while importing %s:" % model_name + ''.join(traceback.format_exception(*sys.exc_info())[1:]))
