Index: django/conf/project_template/settings.py
===================================================================
--- django/conf/project_template/settings.py	(revision 8255)
+++ django/conf/project_template/settings.py	(working copy)
@@ -57,12 +57,22 @@
 #     'django.template.loaders.eggs.load_template_source',
 )
 
-MIDDLEWARE_CLASSES = (
-    'django.middleware.common.CommonMiddleware',
-    'django.contrib.sessions.middleware.SessionMiddleware',
-    'django.contrib.auth.middleware.AuthenticationMiddleware',
-    'django.middleware.doc.XViewMiddleware',
-)
+MIDDLEWARE_CLASSES = {
+    'request': (
+        'django.middleware.common.CommonMiddleware',
+        'django.contrib.sessions.middleware.SessionMiddleware',
+        'django.contrib.auth.middleware.AuthenticationMiddleware',
+    ),
+    'response': (
+        'django.contrib.sessions.middleware.SessionMiddleware',
+#        'django.middleware.http.ConditionalGetMiddleware',
+#        'django.middleware.gzip.GZipMiddleware',
+        'django.middleware.common.CommonMiddleware',
+    ),
+    'view': (
+        'django.middleware.doc.XViewMiddleware',
+    ),
+}
 
 ROOT_URLCONF = '{{ project_name }}.urls'
 
Index: django/conf/global_settings.py
===================================================================
--- django/conf/global_settings.py	(revision 8255)
+++ django/conf/global_settings.py	(working copy)
@@ -291,17 +291,24 @@
 # MIDDLEWARE #
 ##############
 
-# List of middleware classes to use.  Order is important; in the request phase,
-# this middleware classes will be applied in the order given, and in the
-# response phase the middleware will be applied in reverse order.
-MIDDLEWARE_CLASSES = (
-    'django.contrib.sessions.middleware.SessionMiddleware',
-    'django.contrib.auth.middleware.AuthenticationMiddleware',
-#     'django.middleware.http.ConditionalGetMiddleware',
-#     'django.middleware.gzip.GZipMiddleware',
-    'django.middleware.common.CommonMiddleware',
-    'django.middleware.doc.XViewMiddleware',
-)
+# List of middleware classes to use.  Order is important; all middleware types
+# will be applied in the order given.
+MIDDLEWARE_CLASSES = {
+    'request': (
+        'django.middleware.common.CommonMiddleware',
+        'django.contrib.sessions.middleware.SessionMiddleware',
+        'django.contrib.auth.middleware.AuthenticationMiddleware',
+    ),
+    'response': (
+        'django.contrib.sessions.middleware.SessionMiddleware',
+#        'django.middleware.http.ConditionalGetMiddleware',
+#        'django.middleware.gzip.GZipMiddleware',
+        'django.middleware.common.CommonMiddleware',
+    ),
+    'view': (
+        'django.middleware.doc.XViewMiddleware',
+    ),
+}
 
 ############
 # SESSIONS #
Index: django/core/handlers/base.py
===================================================================
--- django/core/handlers/base.py	(revision 8255)
+++ django/core/handlers/base.py	(working copy)
@@ -1,6 +1,7 @@
 import sys
 
 from django import http
+from django.core import exceptions
 from django.core import signals
 from django.utils.encoding import force_unicode
 
@@ -16,38 +17,14 @@
     def __init__(self):
         self._request_middleware = self._view_middleware = self._response_middleware = self._exception_middleware = None
 
-    def load_middleware(self):
-        """
-        Populate middleware lists from settings.MIDDLEWARE_CLASSES.
-
-        Must be called after the environment is fixed (see __call__).
-        """
-        from django.conf import settings
-        from django.core import exceptions
+    def _load_middleware_from_list(self, mw_paths):
         self._request_middleware = []
         self._view_middleware = []
         self._response_middleware = []
         self._exception_middleware = []
-        for middleware_path in settings.MIDDLEWARE_CLASSES:
-            try:
-                dot = middleware_path.rindex('.')
-            except ValueError:
-                raise exceptions.ImproperlyConfigured, '%s isn\'t a middleware module' % middleware_path
-            mw_module, mw_classname = middleware_path[:dot], middleware_path[dot+1:]
-            try:
-                mod = __import__(mw_module, {}, {}, [''])
-            except ImportError, e:
-                raise exceptions.ImproperlyConfigured, 'Error importing middleware %s: "%s"' % (mw_module, e)
-            try:
-                mw_class = getattr(mod, mw_classname)
-            except AttributeError:
-                raise exceptions.ImproperlyConfigured, 'Middleware module "%s" does not define a "%s" class' % (mw_module, mw_classname)
-
-            try:
-                mw_instance = mw_class()
-            except exceptions.MiddlewareNotUsed:
-                continue
-
+        mw_instances = [ _middleware_instance_from_path(mw_path) for mw_path
+                in mw_paths ]
+        for mw_instance in mw_instances:
             if hasattr(mw_instance, 'process_request'):
                 self._request_middleware.append(mw_instance.process_request)
             if hasattr(mw_instance, 'process_view'):
@@ -57,9 +34,42 @@
             if hasattr(mw_instance, 'process_exception'):
                 self._exception_middleware.insert(0, mw_instance.process_exception)
 
+    def _load_middleware_from_dict(self, mw_dict):
+        # fill the instances dict so that we have only one instance of each
+        # middleware
+        mw_instances = {}
+        for mw_path_tuple in mw_dict.values():
+            for mw_path in mw_path_tuple:
+                if mw_path not in mw_instances:
+                    mw_instances[mw_path] = _middleware_instance_from_path(mw_path)
+
+        self._request_middleware = _middleware_type_from_dicts('request',
+                mw_instances, mw_dict)
+        self._view_middleware = _middleware_type_from_dicts('view',
+                mw_instances, mw_dict)
+        self._response_middleware = _middleware_type_from_dicts('response',
+                mw_instances, mw_dict)
+        self._exception_middleware = _middleware_type_from_dicts('exception',
+                mw_instances, mw_dict)
+
+    def load_middleware(self):
+        """
+        Populate middleware lists from settings.MIDDLEWARE_CLASSES.
+
+        Must be called after the environment is fixed (see __call__).
+        """
+        from django.conf import settings
+        if isinstance(settings.MIDDLEWARE_CLASSES, (tuple, list)):
+            self._load_middleware_from_list(settings.MIDDLEWARE_CLASSES)
+        elif isinstance(settings.MIDDLEWARE_CLASSES, (dict)):
+            self._load_middleware_from_dict(settings.MIDDLEWARE_CLASSES)
+        else:
+            raise ImproperlyConfigured("MIDDLEWARE_CLASSES setting has to be"
+                    "either a tuple, list or a dict.")
+
     def get_response(self, request):
         "Returns an HttpResponse object for the given HttpRequest"
-        from django.core import exceptions, urlresolvers
+        from django.core import urlresolvers
         from django.conf import settings
 
         # Apply request middleware
@@ -195,3 +205,35 @@
         return force_unicode(script_url[:-len(environ.get('PATH_INFO', ''))])
     return force_unicode(environ.get('SCRIPT_NAME', u''))
 
+def _middleware_instance_from_path(middleware_path):
+    try:
+        dot = middleware_path.rindex('.')
+    except ValueError:
+        raise exceptions.ImproperlyConfigured, '%s isn\'t a middleware module' % middleware_path
+    mw_module, mw_classname = middleware_path[:dot], middleware_path[dot+1:]
+    try:
+        mod = __import__(mw_module, {}, {}, [''])
+    except ImportError, e:
+        raise exceptions.ImproperlyConfigured, 'Error importing middleware %s: "%s"' % (mw_module, e)
+    try:
+        mw_class = getattr(mod, mw_classname)
+    except AttributeError:
+        raise exceptions.ImproperlyConfigured, 'Middleware module "%s" does not define a "%s" class' % (mw_module, mw_classname)
+    # I'm ignoring the MiddlewareNotUsed exception as it's not used in Django
+    # according to grep, not documented and the only use case I can see is to
+    # raise it in custom middleware __init__ to disable the middleware
+    # temporarily. That can be better achieved with disabling it in settings
+    # (where it is explicit and visible). If everybody is happy with that, the
+    # exception should be removed from core.exceptions. See also #7820.
+    return mw_class()
+
+def _middleware_type_from_dicts(mw_type, mw_instance_dict, mw_dict):
+    mw_instance_list = []
+    for mw_path in mw_dict.get(mw_type, []):
+        mw_instance = mw_instance_dict[mw_path]
+        if not hasattr(mw_instance, 'process_' + mw_type):
+            raise ImproperlyConfigured("%s middleware needs a 'process_%s' "
+                    "method, but it's missing from %s." % (mw_type.title(),
+                        mw_type, mw_path))
+        mw_instance_list.append(mw_instance)
+    return mw_instance_list
