=== django/test/simple.py
==================================================================
--- django/test/simple.py	(/mirror/django/trunk)	(revision 5420)

+++ django/test/simple.py	(/local/django/mymultidb)	(revision 5420)

@@ -138,10 +138,11 @@

     for test in extra_tests:
         suite.addTest(test)
 
-    old_name = settings.DATABASE_NAME
+    old_database_name = settings.DATABASE_NAME
+    old_other_databases = settings.OTHER_DATABASES
     create_test_db(verbosity, autoclobber=not interactive)
     result = unittest.TextTestRunner(verbosity=verbosity).run(suite)
-    destroy_test_db(old_name, verbosity)
+    destroy_test_db(old_database_name, old_other_databases, verbosity)
     
     teardown_test_environment()
     
=== django/test/utils.py
==================================================================
--- django/test/utils.py	(/mirror/django/trunk)	(revision 5420)

+++ django/test/utils.py	(/local/django/mymultidb)	(revision 5420)

@@ -1,6 +1,6 @@

 import sys, time, os
-from django.conf import settings
-from django.db import connection, get_creation_module
+from django.conf import settings, UserSettingsHolder
+from django.db import connections, get_creation_module, _default
 from django.core import mail
 from django.core.management import call_command
 from django.dispatch import dispatcher
@@ -78,27 +78,74 @@

     elif hasattr(connection.connection, "set_isolation_level"):
         connection.connection.set_isolation_level(0)
 
-def get_mysql_create_suffix():
+def get_mysql_create_suffix(settings):
     suffix = []
-    if settings.TEST_DATABASE_CHARSET:
-        suffix.append('CHARACTER SET %s' % settings.TEST_DATABASE_CHARSET)
-    if settings.TEST_DATABASE_COLLATION:
-        suffix.append('COLLATE %s' % settings.TEST_DATABASE_COLLATION)
+    if settings.DATABASE_CHARSET:
+        suffix.append('CHARACTER SET %s' % settings.DATABASE_CHARSET)
+    if settings.DATABASE_COLLATION:
+        suffix.append('COLLATE %s' % settings.DATABASE_COLLATION)
     return ' '.join(suffix)
 
 def get_postgresql_create_suffix():
-    assert settings.TEST_DATABASE_COLLATION is None, "PostgreSQL does not support collation setting at database creation time."
-    if settings.TEST_DATABASE_CHARSET:
-        return "WITH ENCODING '%s'" % settings.TEST_DATABASE_CHARSET
+    assert settings.DATABASE_COLLATION is None, "PostgreSQL does not support collation setting at database creation time."
+    if settings.DATABASE_CHARSET:
+        return "WITH ENCODING '%s'" % settings.DATABASE_CHARSET
     return ''
 
 def create_test_db(verbosity=1, autoclobber=False):
+
+    if settings.DATABASE_ENGINE == "sqlite3":
+        if settings.TEST_DATABASE_NAME and settings.TEST_DATABASE_NAME != ":memory:":
+            TEST_DATABASE_NAME = settings.TEST_DATABASE_NAME
+        else:
+            TEST_DATABASE_NAME = ":memory:"
+    else:
+        if settings.TEST_DATABASE_NAME:
+            TEST_DATABASE_NAME = settings.TEST_DATABASE_NAME
+        else:
+            TEST_DATABASE_NAME = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
+
+    settings.DATABASE_NAME = TEST_DATABASE_NAME
+    settings.DATABASE_CHARSET = settings.TEST_DATABASE_CHARSET
+    settings.DATABASE_COLLATION = settings.TEST_DATABASE_COLLATION
+
+    # default database
+    _create_test_db(settings, verbosity=verbosity, autoclobber=autoclobber)
+
+    # other databases
+    settings.OTHER_DATABASES = settings.TEST_OTHER_DATABASES
+    for db in settings.OTHER_DATABASES:
+        info = settings.OTHER_DATABASES[db]
+        database = UserSettingsHolder(settings)
+        for k, v in info.items():
+            setattr(database, k, v)
+        _create_test_db(database, verbosity=verbosity, autoclobber=autoclobber, connection_name=db)
+
+##    for cnx in connections:
+##        connections[cnx].connection.close()
+
+    call_command('syncdb', verbosity=verbosity, interactive=False)
+
+    if settings.CACHE_BACKEND.startswith('db://'):
+        cache_name = settings.CACHE_BACKEND[len('db://'):]
+        call_command('createcachetable', cache_name)
+
+    # Get a cursor (even though we don't need one yet). This has
+    # the side effect of initializing the test database.
+##    for cnx in connections:
+##        cursor = connections[cnx].connection.cursor()
+##    cursor = connection.cursor()
+
+    return TEST_DATABASE_NAME
+
+def _create_test_db(settings, verbosity=1, autoclobber=False, connection_name=_default):
     """
     Creates a test database, prompting the user for confirmation if the
     database already exists. Returns the name of the test database created.
     """
+    connection = connections[connection_name].connection
     # If the database backend wants to create the test DB itself, let it
-    creation_module = get_creation_module()
+    creation_module = connections[connection_name].get_creation_module()
     if hasattr(creation_module, "create_test_db"):
         creation_module.create_test_db(settings, connection, verbosity, autoclobber)
         return
@@ -109,19 +156,18 @@

     # in-memory database. Using the TEST_DATABASE_NAME setting you can still choose
     # to run on a physical database.
     if settings.DATABASE_ENGINE == "sqlite3":
-        if settings.TEST_DATABASE_NAME and settings.TEST_DATABASE_NAME != ":memory:":
-            TEST_DATABASE_NAME = settings.TEST_DATABASE_NAME
+        if settings.DATABASE_NAME != ":memory:":
             # Erase the old test database
             if verbosity >= 1:
                 print "Destroying old test database..."
-            if os.access(TEST_DATABASE_NAME, os.F_OK):
+            if os.access(settings.DATABASE_NAME, os.F_OK):
                 if not autoclobber:
-                    confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % TEST_DATABASE_NAME)
+                    confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % settings.DATABASE_NAME)
                 if autoclobber or confirm == 'yes':
                   try:
                       if verbosity >= 1:
                           print "Destroying old test database..."
-                      os.remove(TEST_DATABASE_NAME)
+                      os.remove(settings.DATABASE_NAME)
                   except Exception, e:
                       sys.stderr.write("Got an error deleting the old test database: %s\n" % e)
                       sys.exit(2)
@@ -130,20 +176,15 @@

                     sys.exit(1)
             if verbosity >= 1:
                 print "Creating test database..."
-        else:
-            TEST_DATABASE_NAME = ":memory:"
     else:
         suffix = {
             'postgresql': get_postgresql_create_suffix,
             'postgresql_psycopg2': get_postgresql_create_suffix,
             'mysql': get_mysql_create_suffix,
             'mysql_old': get_mysql_create_suffix,
-        }.get(settings.DATABASE_ENGINE, lambda: '')()
-        if settings.TEST_DATABASE_NAME:
-            TEST_DATABASE_NAME = settings.TEST_DATABASE_NAME
-        else:
-            TEST_DATABASE_NAME = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
+        }.get(settings.DATABASE_ENGINE, lambda: '')(settings)
 
+        connection = connections[connection_name].connection
         qn = connection.ops.quote_name
 
         # Create the test database and connect to it. We need to autocommit
@@ -172,37 +213,40 @@

                 print "Tests cancelled."
                 sys.exit(1)
 
-    connection.close()
-    settings.DATABASE_NAME = TEST_DATABASE_NAME
 
-    call_command('syncdb', verbosity=verbosity, interactive=False)
+def destroy_test_db(old_database_name, old_databases, verbosity=1):
+    if verbosity >= 1:
+        print "Destroying test database(s)..."
+##    connection.close()
+    for cnx in connections.keys():
+        connections[cnx].close()
+##        connections.reset()
+    TEST_DATABASE_NAME = settings.DATABASE_NAME
+    settings.DATABASE_NAME = old_database_name
+    _destroy_test_db(settings, TEST_DATABASE_NAME, verbosity=verbosity)
+    TEST_OTHER_DATABASES = settings.OTHER_DATABASES
+    settings.OTHER_DATABASES = old_databases
+    for db in TEST_OTHER_DATABASES:
+        info = TEST_OTHER_DATABASES[db]
+        database = UserSettingsHolder(settings)
+        for k, v in info.items():
+            setattr(database, k, v)
+        if db in old_databases:
+            _destroy_test_db(database, info['DATABASE_NAME'], verbosity=verbosity, connection_name=db)
 
-    if settings.CACHE_BACKEND.startswith('db://'):
-        cache_name = settings.CACHE_BACKEND[len('db://'):]
-        call_command('createcachetable', cache_name)
-
-    # Get a cursor (even though we don't need one yet). This has
-    # the side effect of initializing the test database.
-    cursor = connection.cursor()
-
-    return TEST_DATABASE_NAME
-
-def destroy_test_db(old_database_name, verbosity=1):
+def _destroy_test_db(settings, TEST_DATABASE_NAME, verbosity=1, connection_name=_default):
+    connection = connections[connection_name].connection
     # If the database wants to drop the test DB itself, let it
-    creation_module = get_creation_module()
+    creation_module = connections[connection_name].get_creation_module()
     if hasattr(creation_module, "destroy_test_db"):
         creation_module.destroy_test_db(settings, connection, old_database_name, verbosity)
         return
 
-    if verbosity >= 1:
-        print "Destroying test database..."
-    connection.close()
-    TEST_DATABASE_NAME = settings.DATABASE_NAME
-    settings.DATABASE_NAME = old_database_name
     if settings.DATABASE_ENGINE == "sqlite3":
         if TEST_DATABASE_NAME and TEST_DATABASE_NAME != ":memory:":
             # Remove the SQLite database file
-            os.remove(TEST_DATABASE_NAME)
+##            os.remove(TEST_DATABASE_NAME)
+            pass
     else:
         # Remove the test database to clean up after
         # ourselves. Connect to the previous database (not the test database)
=== django/db/models/sql/where.py
==================================================================
--- django/db/models/sql/where.py	(/mirror/django/trunk)	(revision 5420)

+++ django/db/models/sql/where.py	(/local/django/mymultidb)	(revision 5420)

@@ -4,11 +4,12 @@

 import datetime
 
 from django.utils import tree
-from django.db import connection
 from django.db.models.fields import Field
 from django.db.models.query_utils import QueryWrapper
 from datastructures import EmptyResultSet, FullResultSet
 
+from copy import deepcopy
+
 # Connection types
 AND = 'AND'
 OR = 'OR'
@@ -26,6 +27,20 @@

     """
     default = AND
 
+    def __init__(self, connection, *args, **kwargs):
+        self.connection = connection
+        super(WhereNode, self).__init__(*args, **kwargs)
+
+    def __deepcopy__(self, memo):
+        x = WhereNode.__new__(WhereNode)
+        memo[id(self)] = x
+        for n, v in self.__dict__.iteritems():
+            if n != 'connection':
+                setattr(x, n, deepcopy(v, memo))
+            else:
+                setattr(x, n, self.connection)
+        return x
+
     def as_sql(self, node=None, qn=None):
         """
         Returns the SQL version of the where clause and the value to be
@@ -38,7 +53,7 @@

         if node is None:
             node = self
         if not qn:
-            qn = connection.ops.quote_name
+            qn = self.connection.ops.quote_name
         if not node.children:
             return None, []
         result = []
@@ -99,11 +114,12 @@

             lhs = '%s.%s' % (qn(table_alias), qn(name))
         else:
             lhs = qn(name)
-        db_type = field and field.db_type() or None
-        field_sql = connection.ops.field_cast_sql(db_type) % lhs
 
+        db_type = field and field.db_type(self.connection.connection_name) or None
+        field_sql = self.connection.ops.field_cast_sql(db_type) % lhs
+        
         if isinstance(value, datetime.datetime):
-            cast_sql = connection.ops.datetime_cast_sql()
+            cast_sql = self.connection.ops.datetime_cast_sql()
         else:
             cast_sql = '%s'
 
@@ -116,11 +132,11 @@

         else:
             extra = ''
 
-        if lookup_type in connection.operators:
-            format = "%s %%s %s" % (connection.ops.lookup_cast(lookup_type),
+        if lookup_type in self.connection.operators:
+            format = "%s %%s %s" % (self.connection.ops.lookup_cast(lookup_type),
                     extra)
             return (format % (field_sql,
-                    connection.operators[lookup_type] % cast_sql), params)
+                    self.connection.operators[lookup_type] % cast_sql), params)
 
         if lookup_type == 'in':
             if not value:
@@ -132,15 +148,15 @@

         elif lookup_type in ('range', 'year'):
             return ('%s BETWEEN %%s and %%s' % field_sql, params)
         elif lookup_type in ('month', 'day'):
-            return ('%s = %%s' % connection.ops.date_extract_sql(lookup_type,
+            return ('%s = %%s' % self.connection.ops.date_extract_sql(lookup_type,
                     field_sql), params)
         elif lookup_type == 'isnull':
             return ('%s IS %sNULL' % (field_sql, (not value and 'NOT ' or '')),
                     params)
         elif lookup_type == 'search':
-            return (connection.ops.fulltext_search_sql(field_sql), params)
+            return (self.connection.ops.fulltext_search_sql(field_sql), params)
         elif lookup_type in ('regex', 'iregex'):
-            return connection.ops.regex_lookup(lookup_type) % (field_sql, cast_sql), params
+            return self.connection.ops.regex_lookup(lookup_type) % (field_sql, cast_sql), params
 
         raise TypeError('Invalid lookup_type: %r' % lookup_type)
 
=== django/db/models/sql/query.py
==================================================================
--- django/db/models/sql/query.py	(/mirror/django/trunk)	(revision 5420)

+++ django/db/models/sql/query.py	(/local/django/mymultidb)	(revision 5420)

@@ -60,7 +60,7 @@

         # SQL-related attributes
         self.select = []
         self.tables = []    # Aliases in the order they are created.
-        self.where = where()
+        self.where = where(self.connection)
         self.where_class = where
         self.group_by = []
         self.having = []
@@ -215,7 +215,7 @@

         obj.related_select_cols = []
         obj.related_select_fields = []
         if obj.distinct and len(obj.select) > 1:
-            obj = self.clone(CountQuery, _query=obj, where=self.where_class(),
+            obj = self.clone(CountQuery, _query=obj, where=self.where_class(self.connection),
                     distinct=False)
             obj.select = []
             obj.extra_select = {}
@@ -346,10 +346,10 @@

                 self.where.add(EverythingNode(), AND)
         elif self.where:
             # rhs has an empty where clause.
-            w = self.where_class()
+            w = self.where_class(self.connection)
             w.add(EverythingNode(), AND)
         else:
-            w = self.where_class()
+            w = self.where_class(self.connection)
         self.where.add(w, connector)
 
         # Selection columns and extra extensions are those provided by 'rhs'.
=== django/db/models/sql/subqueries.py
==================================================================
--- django/db/models/sql/subqueries.py	(/mirror/django/trunk)	(revision 5420)

+++ django/db/models/sql/subqueries.py	(/local/django/mymultidb)	(revision 5420)

@@ -47,7 +47,7 @@

         for related in cls._meta.get_all_related_many_to_many_objects():
             if not isinstance(related.field, generic.GenericRelation):
                 for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
-                    where = self.where_class()
+                    where = self.where_class(self.connection)
                     where.add((None, related.field.m2m_reverse_name(),
                             related.field, 'in',
                             pk_list[offset : offset+GET_ITERATOR_CHUNK_SIZE]),
@@ -55,14 +55,14 @@

                     self.do_query(related.field.m2m_db_table(), where)
 
         for f in cls._meta.many_to_many:
-            w1 = self.where_class()
+            w1 = self.where_class(self.connection)
             if isinstance(f, generic.GenericRelation):
                 from django.contrib.contenttypes.models import ContentType
                 field = f.rel.to._meta.get_field(f.content_type_field_name)
                 w1.add((None, field.column, field, 'exact',
                         ContentType.objects.get_for_model(cls).id), AND)
             for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
-                where = self.where_class()
+                where = self.where_class(self.connection)
                 where.add((None, f.m2m_column_name(), f, 'in',
                         pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE]),
                         AND)
@@ -79,7 +79,7 @@

         lot of values in pk_list.
         """
         for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
-            where = self.where_class()
+            where = self.where_class(self.connection)
             field = self.model._meta.pk
             where.add((None, field.column, field, 'in',
                     pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE]), AND)
@@ -178,7 +178,7 @@

 
         # Now we adjust the current query: reset the where clause and get rid
         # of all the tables we don't need (since they're in the sub-select).
-        self.where = self.where_class()
+        self.where = self.where_class(self.connection)
         if self.related_updates or must_pre_select:
             # Either we're using the idents in multiple update queries (so
             # don't want them to change), or the db backend doesn't support
@@ -202,7 +202,7 @@

         This is used by the QuerySet.delete_objects() method.
         """
         for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
-            self.where = self.where_class()
+            self.where = self.where_class(self.connection)
             f = self.model._meta.pk
             self.where.add((None, f.column, f, 'in',
                     pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE]),
=== django/db/models/base.py
==================================================================
--- django/db/models/base.py	(/mirror/django/trunk)	(revision 5420)

+++ django/db/models/base.py	(/local/django/mymultidb)	(revision 5420)

@@ -12,7 +12,7 @@

 from django.db.models.fields.related import OneToOneRel, ManyToOneRel, OneToOneField
 from django.db.models.query import delete_objects, Q
 from django.db.models.options import Options, AdminOptions
-from django.db import connection, transaction
+from django.db import transaction
 from django.db.models import signals
 from django.db.models.loading import register_models, get_model
 from django.dispatch import dispatcher
@@ -329,7 +329,7 @@

                 result = manager._insert(values, return_id=update_pk)
             else:
                 # Create a new record with defaults for everything.
-                result = manager._insert([(meta.pk, connection.ops.pk_default_value())], return_id=update_pk, raw_values=True)
+                result = manager._insert([(meta.pk, manager.db.connection.ops.pk_default_value())], return_id=update_pk, raw_values=True)
 
             if update_pk:
                 setattr(self, meta.pk.attname, result)
@@ -419,7 +419,7 @@

     def _get_next_or_previous_in_order(self, is_next):
         cachename = "__%s_order_cache" % is_next
         if not hasattr(self, cachename):
-            qn = connection.ops.quote_name
+            qn = self._default_manager.db.connection.ops.quote_name
             op = is_next and '>' or '<'
             order = not is_next and '-_order' or '_order'
             order_field = self._meta.order_with_respect_to
=== django/db/models/manager.py
==================================================================
--- django/db/models/manager.py	(/mirror/django/trunk)	(revision 5420)

+++ django/db/models/manager.py	(/local/django/mymultidb)	(revision 5420)

@@ -1,8 +1,9 @@

 import copy
 
 from django.db.models.query import QuerySet, EmptyQuerySet, insert_query
+from django.db import ConnectionInfoDescriptor
 from django.dispatch import dispatcher
-from django.db.models import signals
+from django.db.models import signals, get_apps, get_models
 from django.db.models.fields import FieldDoesNotExist
 
 def ensure_default_manager(sender):
@@ -22,6 +23,8 @@

     # Tracks each time a Manager instance is created. Used to retain order.
     creation_counter = 0
 
+    db = ConnectionInfoDescriptor()
+
     def __init__(self):
         super(Manager, self).__init__()
         # Increase the creation counter, and save our local copy.
=== django/db/models/options.py
==================================================================
--- django/db/models/options.py	(/mirror/django/trunk)	(revision 5420)

+++ django/db/models/options.py	(/local/django/mymultidb)	(revision 5420)

@@ -53,6 +53,8 @@

         self.module_name = self.object_name.lower()
         self.verbose_name = get_verbose_name(self.object_name)
 
+        self.model = cls
+
         # Next, apply any overridden values from 'class Meta'.
         if self.meta:
             meta_attrs = self.meta.__dict__.copy()
@@ -84,8 +86,8 @@

         del self.meta
 
     def _prepare(self, model):
-        from django.db import connection
         from django.db.backends.util import truncate_name
+        from django.db import connections, connection_name_app_model
         if self.order_with_respect_to:
             self.order_with_respect_to = self.get_field(self.order_with_respect_to)
             self.ordering = ('_order',)
@@ -107,7 +109,8 @@

         # If the db_table wasn't provided, use the app_label + module_name.
         if not self.db_table:
             self.db_table = "%s_%s" % (self.app_label, self.module_name)
-            self.db_table = truncate_name(self.db_table, connection.ops.max_name_length())
+            connection_name = connection_name_app_model(self.app_label, self.object_name)
+            self.db_table = truncate_name(self.db_table, connections[connection_name].connection.ops.max_name_length())
 
     def add_field(self, field):
         # Insert the given field in the order in which it was created, using
=== django/db/models/__init__.py
==================================================================
--- django/db/models/__init__.py	(/mirror/django/trunk)	(revision 5420)

+++ django/db/models/__init__.py	(/local/django/mymultidb)	(revision 5420)

@@ -1,7 +1,6 @@

 from django.conf import settings
 from django.core.exceptions import ObjectDoesNotExist, ImproperlyConfigured
 from django.core import validators
-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.query import Q
 from django.db.models.manager import Manager
=== django/db/models/fields/__init__.py
==================================================================
--- django/db/models/fields/__init__.py	(/mirror/django/trunk)	(revision 5420)

+++ django/db/models/fields/__init__.py	(/local/django/mymultidb)	(revision 5420)

@@ -7,7 +7,7 @@

 except ImportError:
     from django.utils import _decimal as decimal    # for Python 2.3
 
-from django.db import get_creation_module
+from django.db import connections
 from django.db.models import signals
 from django.db.models.query_utils import QueryWrapper
 from django.dispatch import dispatcher
@@ -144,7 +144,7 @@

         """
         return value
 
-    def db_type(self):
+    def db_type(self, connection):
         """
         Returns the database column data type for this field, taking into
         account the DATABASE_ENGINE setting.
@@ -165,7 +165,7 @@

         # can implement db_type() instead of get_internal_type() to specify
         # exactly which wacky database column type you want to use.
         try:
-            return get_creation_module().DATA_TYPES[self.get_internal_type()] % self.__dict__
+            return connections[connection].get_creation_module().DATA_TYPES[self.get_internal_type()] % self.__dict__
         except KeyError:
             return None
 
=== django/db/models/fields/related.py
==================================================================
--- django/db/models/fields/related.py	(/mirror/django/trunk)	(revision 5420)

+++ django/db/models/fields/related.py	(/local/django/mymultidb)	(revision 5420)

@@ -1,4 +1,4 @@

-from django.db import connection, transaction
+from django.db import transaction
 from django.db.models import signals, get_model
 from django.db.models.fields import AutoField, Field, IntegerField, PositiveIntegerField, PositiveSmallIntegerField, get_ul_class
 from django.db.models.related import RelatedObject
@@ -368,6 +368,8 @@

             # target_col_name: the PK colname in join_table for the target object
             # *objs - objects to add. Either object instances, or primary keys of object instances.
 
+            connection = self.model._default_manager.db.connection
+
             # If there aren't any objects, there is nothing to do.
             if objs:
                 # Check that all the objects are of the right type
@@ -391,13 +393,15 @@

                     cursor.execute("INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \
                         (self.join_table, source_col_name, target_col_name),
                         [self._pk_val, obj_id])
-                transaction.commit_unless_managed()
+                transaction.commit_unless_managed(connection)
 
         def _remove_items(self, source_col_name, target_col_name, *objs):
             # source_col_name: the PK colname in join_table for the source object
             # target_col_name: the PK colname in join_table for the target object
             # *objs - objects to remove
 
+            connection = self.model._default_manager.db.connection
+
             # If there aren't any objects, there is nothing to do.
             if objs:
                 # Check that all the objects are of the right type
@@ -413,15 +417,16 @@

                     (self.join_table, source_col_name,
                     target_col_name, ",".join(['%s'] * len(old_ids))),
                     [self._pk_val] + list(old_ids))
-                transaction.commit_unless_managed()
+                transaction.commit_unless_managed(connection)
 
         def _clear_items(self, source_col_name):
             # source_col_name: the PK colname in join_table for the source object
+            connection = self.model._default_manager.db.connection
             cursor = connection.cursor()
             cursor.execute("DELETE FROM %s WHERE %s = %%s" % \
                 (self.join_table, source_col_name),
                 [self._pk_val])
-            transaction.commit_unless_managed()
+            transaction.commit_unless_managed(connection)
 
     return ManyRelatedManager
 
@@ -445,7 +450,7 @@

         superclass = rel_model._default_manager.__class__
         RelatedManager = create_many_related_manager(superclass)
 
-        qn = connection.ops.quote_name
+        qn = rel_model._default_manager.db.connection.ops.quote_name
         manager = RelatedManager(
             model=rel_model,
             core_filters={'%s__pk' % self.related.field.name: instance._get_pk_val()},
@@ -486,7 +491,7 @@

         superclass = rel_model._default_manager.__class__
         RelatedManager = create_many_related_manager(superclass)
 
-        qn = connection.ops.quote_name
+        qn = rel_model._default_manager.db.connection.ops.quote_name
         manager = RelatedManager(
             model=rel_model,
             core_filters={'%s__pk' % self.field.related_query_name(): instance._get_pk_val()},
@@ -673,15 +678,15 @@

         defaults.update(kwargs)
         return super(ForeignKey, self).formfield(**defaults)
 
-    def db_type(self):
+    def db_type(self, connection):
         # The database column type of a ForeignKey is the column type
         # of the field to which it points. An exception is if the ForeignKey
         # points to an AutoField/PositiveIntegerField/PositiveSmallIntegerField,
         # in which case the column type is simply that of an IntegerField.
         rel_field = self.rel.get_related_field()
         if isinstance(rel_field, (AutoField, PositiveIntegerField, PositiveSmallIntegerField)):
-            return IntegerField().db_type()
-        return rel_field.db_type()
+            return IntegerField().db_type(connection)
+        return rel_field.db_type(connection)
 
 class OneToOneField(ForeignKey):
     """
@@ -828,7 +833,7 @@

             defaults['initial'] = [i._get_pk_val() for i in defaults['initial']]
         return super(ManyToManyField, self).formfield(**defaults)
 
-    def db_type(self):
+    def db_type(self, connection):
         # A ManyToManyField is not represented by a single column,
         # so return None.
         return None
=== django/db/models/query.py
==================================================================
--- django/db/models/query.py	(/mirror/django/trunk)	(revision 5420)

+++ django/db/models/query.py	(/local/django/mymultidb)	(revision 5420)

@@ -1,7 +1,7 @@

 import warnings
 
 from django.conf import settings
-from django.db import connection, transaction, IntegrityError
+from django.db import transaction, IntegrityError
 from django.db.models.fields import DateField, FieldDoesNotExist
 from django.db.models.query_utils import Q
 from django.db.models import signals, sql
@@ -20,7 +20,7 @@

     "Represents a lazy database lookup for a set of objects"
     def __init__(self, model=None, query=None):
         self.model = model
-        self.query = query or sql.Query(self.model, connection)
+        self.query = query or sql.Query(self.model, self.model._default_manager.db.connection)
         self._result_cache = None
         self._iter = None
 
@@ -689,10 +689,10 @@

                     instance=instance)
 
         pk_list = [pk for pk,instance in seen_objs[cls]]
-        del_query = sql.DeleteQuery(cls, connection)
+        del_query = sql.DeleteQuery(cls, cls._default_manager.db.connection)
         del_query.delete_batch_related(pk_list)
 
-        update_query = sql.UpdateQuery(cls, connection)
+        update_query = sql.UpdateQuery(cls, cls._default_manager.db.connection)
         for field in cls._meta.fields:
             if field.rel and field.null and field.rel.to in seen_objs:
                 update_query.clear_related(field, pk_list)
@@ -701,7 +701,7 @@

     for cls in ordered_classes:
         seen_objs[cls].reverse()
         pk_list = [pk for pk,instance in seen_objs[cls]]
-        del_query = sql.DeleteQuery(cls, connection)
+        del_query = sql.DeleteQuery(cls, cls._default_manager.db.connection)
         del_query.delete_batch(pk_list)
 
         # Last cleanup; set NULLs where there once was a reference to the
@@ -724,7 +724,7 @@

     the InsertQuery class and is how Model.save() is implemented. It is not
     part of the public API.
     """
-    query = sql.InsertQuery(model, connection)
+    query = sql.InsertQuery(model, model._default_manager.db.connection)
     query.insert_values(values, raw_values)
     return query.execute_sql(return_id)
 
=== django/db/__init__.py
==================================================================
--- django/db/__init__.py	(/mirror/django/trunk)	(revision 5420)

+++ django/db/__init__.py	(/local/django/mymultidb)	(revision 5420)

@@ -1,74 +1,428 @@

 import os
-from django.conf import settings
+from django.conf import settings, UserSettingsHolder
 from django.core import signals
 from django.core.exceptions import ImproperlyConfigured
 from django.dispatch import dispatcher
 from django.utils.functional import curry
 
+try:
+    # Only exists in Python 2.4+
+    from threading import local
+except ImportError:
+    # Import copy of _thread_local.py from Python 2.4
+    from django.utils._threading_local import local
+
 __all__ = ('backend', 'connection', 'DatabaseError', 'IntegrityError')
 
+
+# singleton to represent the default connection in connections
+class dummy(object):
+    def __repr__(self):
+        return self.__str__()
+    def __str__(self):
+        return '<default>'
+_default = dummy()
+del dummy
+
+
+# storage for local default connection
+_local = local()
+
 if not settings.DATABASE_ENGINE:
     settings.DATABASE_ENGINE = 'dummy'
 
-try:
-    # Most of the time, the database backend will be one of the official
-    # backends that ships with Django, so look there first.
-    _import_path = 'django.db.backends.'
-    backend = __import__('%s%s.base' % (_import_path, settings.DATABASE_ENGINE), {}, {}, [''])
-    creation = __import__('%s%s.creation' % (_import_path, settings.DATABASE_ENGINE), {}, {}, [''])
-except ImportError, e:
-    # If the import failed, we might be looking for a database backend
-    # distributed external to Django. So we'll try that next.
-    try:
-        _import_path = ''
-        backend = __import__('%s.base' % settings.DATABASE_ENGINE, {}, {}, [''])
-        creation = __import__('%s.creation' % settings.DATABASE_ENGINE, {}, {}, [''])
-    except ImportError, e_user:
-        # The database backend wasn't found. Display a helpful error message
-        # listing all possible (built-in) database backends.
-        backend_dir = os.path.join(__path__[0], 'backends')
-        available_backends = [f for f in os.listdir(backend_dir) if not f.startswith('_') and not f.startswith('.') and not f.endswith('.py') and not f.endswith('.pyc')]
-        available_backends.sort()
-        if settings.DATABASE_ENGINE not in available_backends:
-            raise ImproperlyConfigured, "%r isn't an available database backend. Available options are: %s" % \
-                (settings.DATABASE_ENGINE, ", ".join(map(repr, available_backends)))
+
+def connect(settings, name, **kw):
+    """Connect to the database specified in settings. Returns a
+    ConnectionInfo on success, raises ImproperlyConfigured if the
+    settings don't specify a valid database connection.
+    """
+    return ConnectionInfo(settings, name, **kw)
+
+
+class ConnectionInfo(object):
+    """Encapsulates all information about a connection and the backend
+    to which it belongs. Provides methods for loading backend
+    creation, introspection, and shell modules, closing the
+    connection, and resetting the connection's query log.
+    """
+    def __init__(self, settings=None, name=_default, **kw):
+        super(ConnectionInfo, self).__init__(**kw)
+        if settings is None:
+            from django.conf import settings
+        if not settings.DATABASE_OPTIONS:
+            settings.DATABASE_OPTIONS = {}
+        self.settings = settings
+        self.name = name
+        self.backend = self.load_backend()
+        self.connection = self.backend.DatabaseWrapper(settings, name)
+        self.DatabaseError = self.backend.DatabaseError
+
+        # Register an event that closes the database connection
+        # when a Django request is finished.
+        dispatcher.connect(self.close, signal=signals.request_finished)
+
+        # Register an event that resets connection.queries
+        # when a Django request is started.
+        dispatcher.connect(self.reset_queries, signal=signals.request_started)
+
+    def __repr__(self):
+        return "Connection: %s, %r (ENGINE=%s NAME=%s)" \
+               % (self.name, self.connection,
+                  self.settings.DATABASE_ENGINE,
+                  self.settings.DATABASE_NAME)
+
+    def name(self):
+        return self.name
+
+    def close(self):
+        """Close connection"""
+        self.connection.close()
+
+    def _import_database_module(self, import_path='', module_name=''):
+        """Lazily import a database module when requested."""
+        return __import__('%s%s.%s' % (import_path, self.settings.DATABASE_ENGINE, module_name), {}, {}, [''])
+
+    # We don't want to import the introspect module unless someone asks for it, so
+    # lazily load it on demmand.
+    def get_introspection_module(self):
+        return self._import_database_module(self._import_path, 'introspection')
+
+    def get_creation_module(self):
+        return self._import_database_module(self._import_path, 'creation')
+
+    def load_backend(self):
+        try:
+            # Most of the time, the database backend will be one of the official
+            # backends that ships with Django, so look there first.
+            self._import_path = 'django.db.backends.'
+            backend = __import__('%s%s.base' % (self._import_path,
+                            self.settings.DATABASE_ENGINE), {}, {}, [''])
+        except ImportError, e:
+            # If the import failed, we might be looking for a database backend
+            # distributed external to Django. So we'll try that next.
+            try:
+                _import_path = ''
+                backend = __import__('%s.base' % self.settings.DATABASE_ENGINE, {}, {}, [''])
+            except ImportError, e_user:
+                # The database backend wasn't found. Display a helpful error message
+                # listing all possible (built-in) database backends.
+                backend_dir = os.path.join(__path__[0], 'backends')
+                available_backends = [f for f in os.listdir(backend_dir) if not f.startswith('_') and not f.startswith('.') and not f.endswith('.py') and not f.endswith('.pyc')]
+                available_backends.sort()
+                if self.settings.DATABASE_ENGINE not in available_backends:
+                    raise ImproperlyConfigured, "%r isn't an available database backend. Available options are: %s" % \
+                        (self.settings.DATABASE_ENGINE, ", ".join(map(repr, available_backends)))
+                else:
+                    raise # If there's some other error, this must be an error in Django itself.
+        return backend
+
+    # We want runshell() to work the same way, but we have to treat it a
+    # little differently (since it just runs instead of returning a module like
+    # the above) and wrap the lazily-loaded runshell() method.
+    def runshell(self):
+        return lambda: _import_database_module(_import_path, "client").runshell()
+
+    def reset_queries(self):
+        """Reset log of queries executed by connection"""
+        self.connection.queries = []
+
+class LazyConnectionManager(object):
+    """Manages named connections lazily, instantiating them as
+    they are requested.
+    """
+    def __init__(self):
+        self.local = local()
+        self.local.connections = {}
+
+        # Reset connections on request finish, to make sure each request can
+        # load the correct connections for its settings
+        dispatcher.connect(self.reset, signal=signals.request_finished)
+
+    def __iter__(self):
+        # Iterates only over *active* connections, not all possible
+        # connections
+        return iter(self.local.connections.keys())
+
+    def __getattr__(self, attr):
+        return getattr(self.local.connections, attr)
+
+    def __getitem__(self, k):
+        try:
+            return self.local.connections[k]
+        except (AttributeError, KeyError):
+            return self.connect(k)
+
+    def __setitem__(self, k, v):
+        try:
+            self.local.connections[k] = v
+        except AttributeError:
+            # First access in thread
+            self.local.connections = {k: v}
+
+    def connect(self, name):
+        """Return the connection with this name in
+        settings.OTHER_DATABASES. Creates the connection if it doesn't yet
+        exist. Reconnects if it does. If the name requested is the default
+        connection (a singleton defined in django.db), then the default
+        connection is returned.
+        """
+        try:
+            cnx = self.local.connections
+        except AttributeError:
+            cnx = self.local.connections = {}
+        if name in cnx:
+            cnx[name].close()
+        if name is _default:
+            cnx[name] = connect(settings, _default)
+            return cnx[name]
+        try:
+            info = settings.OTHER_DATABASES[name]
+        except KeyError:
+            raise ImproperlyConfigured, \
+                  "No database connection '%s' has been configured" % name
+        except AttributeError:
+            raise ImproperlyConfigured, \
+                  "No OTHER_DATABASES in settings."
+
+        # In settings it's a dict, but connect() needs an object:
+        # pass global settings so that the default connection settings
+        # can be defaults for the named connections.
+        database = UserSettingsHolder(settings)
+        for k, v in info.items():
+            setattr(database, k, v)
+        cnx[name] = connect(database, name)
+        return cnx[name]
+
+    def items(self):
+        # Iterates over *all possible* connections
+        items = []
+        for key in self.keys():
+            items.append((key, self[key]))
+        return items
+
+    def keys(self):
+        # Iterates over *all possible* connections
+        keys = [_default]
+        try:
+            keys.extend(settings.OTHER_DATABASES.keys())
+        except AttributeError:
+            pass
+        return keys
+
+    def reset(self):
+        if not hasattr(self.local, 'connections'):
+            return
+        if settings.DATABASE_NAME == ':memory:':
+##            try:
+##                cnx = self.local.connections
+##            except AttributeError:
+##                cnx = self.local.connections = {}
+            self.local.connections = {_default: self.local.connections[_default]}
+##            for c in cnx:
+##                if c != _default:
+##                    del self.local.connections[c]
         else:
-            raise # If there's some other error, this must be an error in Django itself.
+            self.local.connections = {}
 
-def _import_database_module(import_path='', module_name=''):
-    """Lazily import a database module when requested."""
-    return __import__('%s%s.%s' % (import_path, settings.DATABASE_ENGINE, module_name), {}, {}, [''])
+def model_connection_name(klass):
+    """Get the connection name that a model is configured to use, with the
+    current settings.
+    """
+    app = klass._meta.app_label
+    model = klass.__name__
+    app_model = "%s.%s" % (app, model)
 
-# We don't want to import the introspect module unless someone asks for it, so
-# lazily load it on demmand.
-get_introspection_module = curry(_import_database_module, _import_path, 'introspection')
+    # Quick exit if no OTHER_DATABASES defined
+    if (not hasattr(settings, 'OTHER_DATABASES')
+        or not settings.OTHER_DATABASES):
+        return _default
+    # Look in MODELS for the best match: app_label.Model. If that isn't
+    # found, take just app_label. If nothing is found, use the default
+    maybe = None
+    for name, db_def in settings.OTHER_DATABASES.items():
+        if not 'MODELS' in db_def:
+            continue
+        mods = db_def['MODELS']
+        # Can't get a better match than this
+        if app_model in mods:
+            return name
+        elif app in mods:
+            if maybe is not None:
+                raise ImproperlyConfigured, \
+                    "App %s appears in more than one OTHER_DATABASES " \
+                    "setting (%s and %s)" % (maybe, name)
+            maybe = name
+    if maybe:
+        return maybe
+    # No named connection for this model; use the default
+    return _default
 
-def get_creation_module():
-    return creation
+def connection_name_app_model(app_label, model_name):
+    """Get the connection name that a model is configured to use, with the
+    current settings.
+    """
+    app_model = "%s.%s" % (app_label, model_name)
 
-# We want runshell() to work the same way, but we have to treat it a
-# little differently (since it just runs instead of returning a module like
-# the above) and wrap the lazily-loaded runshell() method.
-runshell = lambda: _import_database_module(_import_path, "client").runshell()
+    # Quick exit if no OTHER_DATABASES defined
+    if (not hasattr(settings, 'OTHER_DATABASES')
+        or not settings.OTHER_DATABASES):
+        return _default
+    # Look in MODELS for the best match: app_label.Model. If that isn't
+    # found, take just app_label. If nothing is found, use the default
+    maybe = None
+    for name, db_def in settings.OTHER_DATABASES.items():
+        if not 'MODELS' in db_def:
+            continue
+        mods = db_def['MODELS']
+        # Can't get a better match than this
+        if app_model in mods:
+            return name
+        elif app_label in mods:
+            if maybe is not None:
+                raise ImproperlyConfigured, \
+                    "App %s appears in more than one OTHER_DATABASES " \
+                    "setting (%s and %s)" % (maybe, name)
+            maybe = name
+    if maybe:
+        return maybe
+    # No named connection for this model; use the default
+    return _default
 
-# Convenient aliases for backend bits.
-connection = backend.DatabaseWrapper(**settings.DATABASE_OPTIONS)
-DatabaseError = backend.DatabaseError
-IntegrityError = backend.IntegrityError
+class ConnectionInfoDescriptor(object):
+    """Descriptor used to access database connection information from a
+    manager or other connection holder. Keeps a thread-local cache of
+    connections per instance, and always returns the same connection for an
+    instance in particular thread during a particular request.
 
-# Register an event that closes the database connection
-# when a Django request is finished.
-dispatcher.connect(connection.close, signal=signals.request_finished)
+    Any object that includes a ``model`` attribute that holds a model class
+    can use this descriptor to manage connections.
+    """
 
-# Register an event that resets connection.queries
-# when a Django request is started.
+    def __init__(self):
+        self.local = local()
+        self.local.cnx = {}
+        dispatcher.connect(self.reset, signal=signals.request_finished)
+
+    def __get__(self, instance, type=None):
+        if instance is None:
+            raise AttributeError, \
+                "ConnectionInfo is accessible only through an instance"
+        try:
+            instance_connection = self.local.cnx.get(instance, None)
+        except AttributeError:
+            # First access in this thread
+            self.local.cnx = {}
+            instance_connection = None
+        if instance_connection is None:
+            instance_connection = self.get_connection(instance)
+            self.local.cnx[instance] = instance_connection
+        return instance_connection
+
+    def __set__(self, instance, value):
+        try:
+            self.local.cnx[instance] = value
+        except AttributeError:
+            # First access in thread
+            self.local.cnx = {instance: value}
+
+    def __delete__(self, instance):
+        try:
+            del self.local.cnx[instance]
+        except (AttributeError, KeyError):
+            # Not stored, no need to reset
+            pass
+
+    def get_connection(self, instance):
+        return connections[model_connection_name(instance.model)]
+
+    def reset(self):
+        if not hasattr(self.local, 'cnx'):
+            return
+        self.local.cnx = {}
+
+class LocalizingProxy:
+    """A lazy-initializing proxy. The proxied object is not
+    initialized until the first attempt to access it. This is used to
+    attach module-level properties to local storage.
+    """
+    def __init__(self, name, storage, func, *arg, **kw):
+        self.__name = name
+        self.__storage = storage
+        self.__func = func
+        self.__arg = arg
+        self.__kw = kw
+
+        # We need to clear out this thread's storage at the end of each
+        # request, in case new settings are loaded with the next
+        def reset(stor=storage, name=name):
+            if hasattr(stor, name):
+                delattr(stor, name)
+        dispatcher.connect(reset, signal=signals.request_finished)
+
+    def __getattr__(self, attr):
+        # Private (__*) attributes are munged
+        if attr.startswith('_LocalizingProxy'):
+            return self.__dict__[attr]
+        try:
+            return getattr(getattr(self.__storage, self.__name), attr)
+        except AttributeError:
+            setattr(self.__storage, self.__name, self.__func(*self.__arg,
+                                                             **self.__kw))
+            return getattr(getattr(self.__storage, self.__name), attr)
+
+    def __setattr__(self, attr, val):
+        # Private (__*) attributes are munged
+        if attr.startswith('_LocalizingProxy'):
+            self.__dict__[attr] = val
+            return
+        try:
+            stor = getattr(self.__storage, self.__name)
+        except AttributeError:
+            stor =  self.__func(*self.__arg)
+            setattr(self.__storage, self.__name, stor)
+        setattr(stor, attr, val)
+
+
+# Create a manager for named connections
+connections = LazyConnectionManager()
+
+# Backwards compatibility: establish the default connection and set the
+# default connection properties at module level, using the lazy proxy so that
+# each thread may have a different default connection, if so configured
+connection_info = LocalizingProxy('connection_info', _local,
+                                  lambda: connections[_default])
+connection = LocalizingProxy('connection', _local,
+                             lambda: connections[_default].connection)
+backend = LocalizingProxy('backend', _local,
+                          lambda: connections[_default].backend)
+DatabaseError = LocalizingProxy('DatabaseError', _local,
+                                lambda: connections[_default].DatabaseError)
+IntegrityError = LocalizingProxy('IntegrityError', _local,
+                                lambda: connections[_default].IntegrityError)
+get_introspection_module = LocalizingProxy(
+    'get_introspection_module', _local,
+    lambda: connections[_default].get_introspection_module)
+get_creation_module = LocalizingProxy(
+    'get_creation_module', _local,
+    lambda: connections[_default].get_creation_module)
+runshell = LocalizingProxy('runshell', _local,
+                           lambda: connections[_default].runshell)
+
+
 def reset_queries():
-    connection.queries = []
-dispatcher.connect(reset_queries, signal=signals.request_started)
+    connections[_default].reset_queries()
+    for c in connections:
+        try:
+            c.reset_queries()
+        except:
+            pass
 
-# Register an event that rolls back the connection
+# Register an event that rolls back all connections
 # when a Django request has an exception.
 def _rollback_on_exception():
     from django.db import transaction
     transaction.rollback_unless_managed()
-dispatcher.connect(_rollback_on_exception, signal=signals.got_request_exception)
+dispatcher.connect(_rollback_on_exception,
+                   signal=signals.got_request_exception)
+
=== django/db/backends/sqlite3/base.py
==================================================================
--- django/db/backends/sqlite3/base.py	(/mirror/django/trunk)	(revision 5420)

+++ django/db/backends/sqlite3/base.py	(/local/django/mymultidb)	(revision 5420)

@@ -118,7 +118,7 @@

         return self.connection.cursor(factory=SQLiteCursorWrapper)
 
     def close(self):
-        from django.conf import settings
+        settings = self.settings
         # If database is in memory, closing the connection destroys the
         # database. To prevent accidental data loss, ignore close requests on
         # an in-memory db.
=== django/db/backends/__init__.py
==================================================================
--- django/db/backends/__init__.py	(/mirror/django/trunk)	(revision 5420)

+++ django/db/backends/__init__.py	(/local/django/mymultidb)	(revision 5420)

@@ -5,15 +5,17 @@

     # Import copy of _thread_local.py from Python 2.4
     from django.utils._threading_local import local
 
-class BaseDatabaseWrapper(local):
+class BaseDatabaseWrapper(object):
     """
     Represents a database connection.
     """
     ops = None
-    def __init__(self, **kwargs):
+    def __init__(self, settings, connection_name):
+        self.settings = settings
+        self.connection_name = connection_name
+        self.options = settings.DATABASE_OPTIONS
         self.connection = None
         self.queries = []
-        self.options = kwargs
 
     def _commit(self):
         if self.connection is not None:
@@ -29,7 +31,7 @@

             self.connection = None
 
     def cursor(self):
-        from django.conf import settings
+        settings = self.settings
         cursor = self._cursor(settings)
         if settings.DEBUG:
             return self.make_debug_cursor(cursor)
=== django/db/transaction.py
==================================================================
--- django/db/transaction.py	(/mirror/django/trunk)	(revision 5420)

+++ django/db/transaction.py	(/local/django/mymultidb)	(revision 5420)

@@ -16,7 +16,6 @@

     import thread
 except ImportError:
     import dummy_thread as thread
-from django.db import connection
 from django.conf import settings
 
 class TransactionManagementError(Exception):
@@ -116,48 +115,69 @@

     Puts the transaction manager into a manual state: managed transactions have
     to be committed explicitly by the user. If you switch off transaction
     management and there is a pending commit/rollback, the data will be
-    commited.
+    commited. Note that managed state applies across all connections.
     """
     thread_ident = thread.get_ident()
     top = state.get(thread_ident, None)
     if top:
         top[-1] = flag
         if not flag and is_dirty():
-            connection._commit()
+            for cx in all_connections():
+                cx._commit()
             set_clean()
     else:
         raise TransactionManagementError("This code isn't under transaction management")
 
-def commit_unless_managed():
+def commit_unless_managed(conns=None):
     """
     Commits changes if the system is not in managed transaction mode.
     """
     if not is_managed():
-        connection._commit()
+        if conns is None:
+            conns = all_connections()
+        else:
+            conns = ensure_connections(conns)
+        for cx in conns:
+            cx._commit()
     else:
         set_dirty()
 
-def rollback_unless_managed():
+def rollback_unless_managed(conns=None):
     """
     Rolls back changes if the system is not in managed transaction mode.
     """
     if not is_managed():
-        connection._rollback()
+        if conns is None:
+            conns = all_connections()
+        else:
+            conns = ensure_connections(conns)
+        for cx in conns:
+            cx._rollback()
     else:
         set_dirty()
 
-def commit():
+def commit(conns=None):
     """
     Does the commit itself and resets the dirty flag.
     """
-    connection._commit()
+    if conns is None:
+        conns = all_connections()
+    else:
+        conns = ensure_connections(conns)
+    for cx in conns:
+        cx._commit()
     set_clean()
 
-def rollback():
+def rollback(conns=None):
     """
     This function does the rollback itself and resets the dirty flag.
     """
-    connection._rollback()
+    if conns is None:
+        conns = all_connections()
+    else:
+        conns = ensure_connections(conns)
+    for cx in conns:
+        cx._rollback()
     set_clean()
 
 ##############
@@ -179,7 +199,7 @@

             leave_transaction_management()
     return _autocommit
 
-def commit_on_success(func):
+def commit_on_success(func, conns = None):
     """
     This decorator activates commit on response. This way, if the view function
     runs successfully, a commit is made; if the viewfunc produces an exception,
@@ -198,7 +218,7 @@

                 raise
             else:
                 if is_dirty():
-                    commit()
+                    commit(conns)
             return res
         finally:
             leave_transaction_management()
@@ -220,3 +240,31 @@

             leave_transaction_management()
 
     return _commit_manually
+
+###########
+# HELPERS #
+###########
+
+def all_connections():
+    from django.db import connection, connections
+    return [connection] + [ c.connection
+                               for c in connections.values() ]
+
+def ensure_connections(val):
+    from django.db import connections
+    conns = []
+    if isinstance(val, basestring):
+        val = [val]
+    try:
+        iter(val)
+    except:
+        val = [val]
+    for cx in val:
+        if hasattr(cx, 'cursor'):
+            conns.append(cx)
+        elif hasattr(cx, 'connection'):
+            conns.append(cx.connection)
+        elif isinstance(cx, basestring):
+            conns.append(connections[cx].connection)
+    return conns
+
=== django/conf/global_settings.py
==================================================================
--- django/conf/global_settings.py	(/mirror/django/trunk)	(revision 5420)

+++ django/conf/global_settings.py	(/local/django/mymultidb)	(revision 5420)

@@ -122,6 +122,9 @@

 DATABASE_PORT = ''             # Set to empty string for default. Not used with sqlite3.
 DATABASE_OPTIONS = {}          # Set to empty dictionary for default.
 
+# Optional named database connections in addition to the default.
+OTHER_DATABASES = {}
+
 # Host for sending e-mail.
 EMAIL_HOST = 'localhost'
 
@@ -352,6 +355,16 @@

 # If None, a name of 'test_' + DATABASE_NAME will be assumed
 TEST_DATABASE_NAME = None
 
+# Tuple of other test databases to create. Names in this tuple
+# are suffixes that will be appended to TEST_DATABASE_NAME
+TEST_DATABASES = []
+
+# Models to assign to each test database. This must be a list of
+# dicts, with each dict key being a name from TEST_DATABASES and value
+# a list of models or app_labels that will use that database.
+TEST_DATABASE_MODELS = []
+
+
 # Strings used to set the character set and collation order for the test
 # database. These values are passed literally to the server, so they are
 # backend-dependent. If None, no special settings are sent (system defaults are
=== django/core/cache/backends/db.py
==================================================================
--- django/core/cache/backends/db.py	(/mirror/django/trunk)	(revision 5420)

+++ django/core/cache/backends/db.py	(/local/django/mymultidb)	(revision 5420)

@@ -1,7 +1,7 @@

 "Database cache backend."
 
 from django.core.cache.backends.base import BaseCache
-from django.db import connection, transaction, DatabaseError
+from django.db import connections, _default, transaction, DatabaseError
 import base64, time
 from datetime import datetime
 try:
@@ -25,7 +25,7 @@

             self._cull_frequency = 3
 
     def get(self, key, default=None):
-        cursor = connection.cursor()
+        cursor = connections[_default].connection.cursor()
         cursor.execute("SELECT cache_key, value, expires FROM %s WHERE cache_key = %%s" % self._table, [key])
         row = cursor.fetchone()
         if row is None:
@@ -46,7 +46,7 @@

     def _base_set(self, mode, key, value, timeout=None):
         if timeout is None:
             timeout = self.default_timeout
-        cursor = connection.cursor()
+        cursor = connections[_default].connection.cursor()
         cursor.execute("SELECT COUNT(*) FROM %s" % self._table)
         num = cursor.fetchone()[0]
         now = datetime.now().replace(microsecond=0)
@@ -67,12 +67,12 @@

             transaction.commit_unless_managed()
 
     def delete(self, key):
-        cursor = connection.cursor()
+        cursor = connections[_default].connection.cursor()
         cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % self._table, [key])
         transaction.commit_unless_managed()
 
     def has_key(self, key):
-        cursor = connection.cursor()
+        cursor = connections[_default].connection.cursor()
         cursor.execute("SELECT cache_key FROM %s WHERE cache_key = %%s" % self._table, [key])
         return cursor.fetchone() is not None
 
=== django/core/management/base.py
==================================================================
--- django/core/management/base.py	(/mirror/django/trunk)	(revision 5420)

+++ django/core/management/base.py	(/local/django/mymultidb)	(revision 5420)

@@ -85,14 +85,7 @@

                 self.validate()
             output = self.handle(*args, **options)
             if output:
-                if self.output_transaction:
-                    # This needs to be imported here, because it relies on settings.
-                    from django.db import connection
-                    if connection.ops.start_transaction_sql():
-                        print self.style.SQL_KEYWORD(connection.ops.start_transaction_sql())
                 print output
-                if self.output_transaction:
-                    print self.style.SQL_KEYWORD("COMMIT;")
         except CommandError, e:
             sys.stderr.write(self.style.ERROR(str('Error: %s\n' % e)))
             sys.exit(1)
@@ -124,23 +117,70 @@

     args = '<appname appname ...>'
 
     def handle(self, *app_labels, **options):
-        from django.db import models
+        from django.db import models, _default, connections
+        from django.core.management.sql import sql_collate
         if not app_labels:
             raise CommandError('Enter at least one appname.')
         try:
             app_list = [models.get_app(app_label) for app_label in app_labels]
         except (ImproperlyConfigured, ImportError), e:
             raise CommandError("%s. Are you sure your INSTALLED_APPS setting is correct?" % e)
-        output = []
+        output = {}
         for app in app_list:
             app_output = self.handle_app(app, **options)
-            if app_output:
-                output.append(app_output)
-        return '\n'.join(output)
+            if isinstance(app_output, dict):
+                for connection_name, statements in app_output.items():
+                    f_output = output.setdefault(connection_name, [])
+                    if self.output_transaction:
+                        if connections[connection_name].connection.ops.start_transaction_sql():
+                            f_output.append(self.style.SQL_KEYWORD(connections[connection_name].connection.ops.start_transaction_sql()))
+                    f_output.extend(statements)
+                    if self.output_transaction:
+                        f_output.append(self.style.SQL_KEYWORD("COMMIT;"))
+            else:
+                f_output = output.setdefault(_default, [])
+                f_output.append(app_output)
 
+        return sql_collate(output)
+##        return '\n'.join(output)
+
     def handle_app(self, app, **options):
         raise NotImplementedError()
 
+class ConnectionCommand(BaseCommand):
+    args = '<connectionname connectionname ...>'
+
+    def handle(self, *connection_labels, **options):
+        from django.conf import settings
+        from django.db import _default, connections
+        from django.core.management.sql import sql_collate
+        if not connection_labels:
+            connection_list = [_default]
+            connection_list.extend(settings.OTHER_DATABASES.keys())
+        else:
+            # add check connection exists
+            connection_list = []
+            for name in connection_labels:
+                if name == '_default':
+                    connection_list.append(_default)
+                elif name in settings.OTHER_DATABASES:
+                    connection_list.append(name)
+        output = {}
+        for connection_name in connection_list:
+            f_output = output.setdefault(connection_name, [])
+            connection_output = self.handle_connection(connection_name, **options)
+            if connection_output:
+                if self.output_transaction:
+                    if connections[connection_name].connection.ops.start_transaction_sql():
+                        f_output.append(self.style.SQL_KEYWORD(connections[connection_name].connection.ops.start_transaction_sql()))
+                f_output.extend(connection_output)
+                if self.output_transaction:
+                    f_output.append(self.style.SQL_KEYWORD("COMMIT;"))
+        return sql_collate(output)
+
+    def handle_connection(self, connection_name, **options):
+        raise NotImplementedError()
+
 class LabelCommand(BaseCommand):
     args = '<label label ...>'
     label = 'label'
=== django/core/management/commands/sqlall.py
==================================================================
--- django/core/management/commands/sqlall.py	(/mirror/django/trunk)	(revision 5420)

+++ django/core/management/commands/sqlall.py	(/local/django/mymultidb)	(revision 5420)

@@ -7,4 +7,4 @@

 
     def handle_app(self, app, **options):
         from django.core.management.sql import sql_all
-        return '\n'.join(sql_all(app, self.style))
+        return sql_all(app, self.style)
=== django/core/management/commands/sqlcustom.py
==================================================================
--- django/core/management/commands/sqlcustom.py	(/mirror/django/trunk)	(revision 5420)

+++ django/core/management/commands/sqlcustom.py	(/local/django/mymultidb)	(revision 5420)

@@ -7,4 +7,5 @@

 
     def handle_app(self, app, **options):
         from django.core.management.sql import sql_custom
-        return '\n'.join(sql_custom(app))
+        return sql_custom(app)
+
=== django/core/management/commands/sql.py
==================================================================
--- django/core/management/commands/sql.py	(/mirror/django/trunk)	(revision 5420)

+++ django/core/management/commands/sql.py	(/local/django/mymultidb)	(revision 5420)

@@ -7,4 +7,4 @@

 
     def handle_app(self, app, **options):
         from django.core.management.sql import sql_create
-        return '\n'.join(sql_create(app, self.style))
+        return sql_create(app, self.style)
=== django/core/management/commands/createcachetable.py
==================================================================
--- django/core/management/commands/createcachetable.py	(/mirror/django/trunk)	(revision 5420)

+++ django/core/management/commands/createcachetable.py	(/local/django/mymultidb)	(revision 5420)

@@ -8,7 +8,7 @@

     requires_model_validation = False
 
     def handle_label(self, tablename, **options):
-        from django.db import connection, transaction, models
+        from django.db import connections, transaction, models, _default
         fields = (
             # "key" is a reserved word in MySQL, so use "cache_key" instead.
             models.CharField(name='cache_key', max_length=255, unique=True, primary_key=True),
@@ -17,9 +17,9 @@

         )
         table_output = []
         index_output = []
-        qn = connection.ops.quote_name
+        qn = connections[_default].connection.ops.quote_name
         for f in fields:
-            field_output = [qn(f.name), f.db_type()]
+            field_output = [qn(f.name), f.db_type(_default)]
             field_output.append("%sNULL" % (not f.null and "NOT " or ""))
             if f.unique:
                 field_output.append("UNIQUE")
@@ -35,7 +35,7 @@

         for i, line in enumerate(table_output):
             full_statement.append('    %s%s' % (line, i < len(table_output)-1 and ',' or ''))
         full_statement.append(');')
-        curs = connection.cursor()
+        curs = connection[_default].connection.cursor()
         curs.execute("\n".join(full_statement))
         for statement in index_output:
             curs.execute(statement)
=== django/core/management/commands/sqlflush.py
==================================================================
--- django/core/management/commands/sqlflush.py	(/mirror/django/trunk)	(revision 5420)

+++ django/core/management/commands/sqlflush.py	(/local/django/mymultidb)	(revision 5420)

@@ -1,10 +1,10 @@

-from django.core.management.base import NoArgsCommand
+from django.core.management.base import ConnectionCommand
 
-class Command(NoArgsCommand):
+class Command(ConnectionCommand):
     help = "Returns a list of the SQL statements required to return all tables in the database to the state they were in just after they were installed."
 
     output_transaction = True
 
-    def handle_noargs(self, **options):
+    def handle_connection(self, connection, **options):
         from django.core.management.sql import sql_flush
-        return '\n'.join(sql_flush(self.style, only_django=True))
+        return sql_flush(connection, self.style, only_django=True)
=== django/core/management/commands/sqlreset.py
==================================================================
--- django/core/management/commands/sqlreset.py	(/mirror/django/trunk)	(revision 5420)

+++ django/core/management/commands/sqlreset.py	(/local/django/mymultidb)	(revision 5420)

@@ -7,4 +7,4 @@

 
     def handle_app(self, app, **options):
         from django.core.management.sql import sql_reset
-        return '\n'.join(sql_reset(app, self.style))
+        return sql_reset(app, self.style)
=== django/core/management/commands/sqlclear.py
==================================================================
--- django/core/management/commands/sqlclear.py	(/mirror/django/trunk)	(revision 5420)

+++ django/core/management/commands/sqlclear.py	(/local/django/mymultidb)	(revision 5420)

@@ -7,4 +7,4 @@

 
     def handle_app(self, app, **options):
         from django.core.management.sql import sql_delete
-        return '\n'.join(sql_delete(app, self.style))
+        return sql_delete(app, self.style)
=== django/core/management/commands/loaddata.py
==================================================================
--- django/core/management/commands/loaddata.py	(/mirror/django/trunk)	(revision 5420)

+++ django/core/management/commands/loaddata.py	(/local/django/mymultidb)	(revision 5420)

@@ -21,7 +21,9 @@

     def handle(self, *fixture_labels, **options):
         from django.db.models import get_apps
         from django.core import serializers
-        from django.db import connection, transaction
+        from django.db import connections, model_connection_name, transaction
+# MULTIDB: TO REMOVE
+        from django.db import connection
         from django.conf import settings
 
         self.style = no_style()
@@ -39,7 +41,7 @@

         # Get a cursor (even though we don't need one yet). This has
         # the side effect of initializing the test database (if
         # it isn't already initialized).
-        cursor = connection.cursor()
+##        cursor = connection.cursor()
 
         # Start transaction management. All fixtures are installed in a
         # single transaction to ensure that all references are resolved.
@@ -121,6 +123,7 @@

                             print "No %s fixture '%s' in %s." % \
                                 (format, fixture_name, humanize(fixture_dir))
 
+# MULTIDB TODO: use sql_sequence_reset
         if object_count > 0:
             sequence_sql = connection.ops.sequence_reset_sql(self.style, models)
             if sequence_sql:
=== django/core/management/commands/inspectdb.py
==================================================================
--- django/core/management/commands/inspectdb.py	(/mirror/django/trunk)	(revision 5420)

+++ django/core/management/commands/inspectdb.py	(/local/django/mymultidb)	(revision 5420)

@@ -1,22 +1,23 @@

-from django.core.management.base import NoArgsCommand, CommandError
+from django.core.management.base import ConnectionCommand, CommandError
 
-class Command(NoArgsCommand):
+class Command(ConnectionCommand):
     help = "Introspects the database tables in the given database and outputs a Django model module."
 
     requires_model_validation = False
 
-    def handle_noargs(self, **options):
+    def handle_connection(self, connection, **options):
         try:
-            for line in self.handle_inspection():
+            for line in self.handle_inspection(connection):
                 print line
         except NotImplementedError:
             raise CommandError("Database inspection isn't supported for the currently selected database backend.")
 
-    def handle_inspection(self):
-        from django.db import connection, get_introspection_module
+    def handle_inspection(self, conn):
+        from django.db import connections, get_introspection_module
         import keyword
 
-        introspection_module = get_introspection_module()
+        connection = connections[conn].connection
+        introspection_module = connections[conn].get_introspection_module()
 
         table2model = lambda table_name: table_name.title().replace('_', '')
 
=== django/core/management/commands/sqlindexes.py
==================================================================
--- django/core/management/commands/sqlindexes.py	(/mirror/django/trunk)	(revision 5420)

+++ django/core/management/commands/sqlindexes.py	(/local/django/mymultidb)	(revision 5420)

@@ -7,4 +7,4 @@

 
     def handle_app(self, app, **options):
         from django.core.management.sql import sql_indexes
-        return '\n'.join(sql_indexes(app, self.style))
+        return sql_indexes(app, self.style)
=== django/core/management/commands/flush.py
==================================================================
--- django/core/management/commands/flush.py	(/mirror/django/trunk)	(revision 5420)

+++ django/core/management/commands/flush.py	(/local/django/mymultidb)	(revision 5420)

@@ -1,9 +1,9 @@

-from django.core.management.base import NoArgsCommand, CommandError
+from django.core.management.base import ConnectionCommand, CommandError
 from django.core.management.color import no_style
 from optparse import make_option
 
-class Command(NoArgsCommand):
-    option_list = NoArgsCommand.option_list + (
+class Command(ConnectionCommand):
+    option_list = ConnectionCommand.option_list + (
         make_option('--verbosity', action='store', dest='verbosity', default='1',
             type='choice', choices=['0', '1', '2'],
             help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'),
@@ -12,9 +12,9 @@

     )
     help = "Executes ``sqlflush`` on the current database."
 
-    def handle_noargs(self, **options):
+    def handle_connection(self, connection, **options):
         from django.conf import settings
-        from django.db import connection, transaction, models
+        from django.db import transaction, models, connections
         from django.dispatch import dispatcher
         from django.core.management.sql import sql_flush, emit_post_sync_signal
 
@@ -31,7 +31,7 @@

             except ImportError:
                 pass
 
-        sql_list = sql_flush(self.style, only_django=True)
+        sql_list = sql_flush(connection, self.style, only_django=True)
 
         if interactive:
             confirm = raw_input("""You have requested a flush of the database.
@@ -39,14 +39,14 @@

 and return each table to the state it was in after syncdb.
 Are you sure you want to do this?
 
-    Type 'yes' to continue, or 'no' to cancel: """ % settings.DATABASE_NAME)
+    Type 'yes' to continue, or 'no' to cancel: """ % connection)
         else:
             confirm = 'yes'
 
         if confirm == 'yes':
             try:
-                cursor = connection.cursor()
                 for sql in sql_list:
+                    cursor = connections[connection].connection.cursor()
                     cursor.execute(sql)
             except Exception, e:
                 transaction.rollback_unless_managed()
@@ -55,7 +55,7 @@

       * At least one of the expected database tables doesn't exist.
       * The SQL was invalid.
     Hint: Look at the output of 'django-admin.py sqlflush'. That's the SQL this command wasn't able to run.
-    The full error: %s""" % (settings.DATABASE_NAME, e))
+    The full error: %s""" % (connection, e))
             transaction.commit_unless_managed()
 
             # Emit the post sync signal. This allows individual
=== django/core/management/commands/reset.py
==================================================================
--- django/core/management/commands/reset.py	(/mirror/django/trunk)	(revision 5420)

+++ django/core/management/commands/reset.py	(/local/django/mymultidb)	(revision 5420)

@@ -10,10 +10,8 @@

     help = "Executes ``sqlreset`` for the given app(s) in the current database."
     args = '[appname ...]'
 
-    output_transaction = True
-
     def handle_app(self, app, **options):
-        from django.db import connection, transaction
+        from django.db import connections, transaction, _default
         from django.conf import settings
         from django.core.management.sql import sql_reset
 
@@ -21,32 +19,37 @@

 
         self.style = no_style()
 
-        sql_list = sql_reset(app, self.style)
+        sql_dict = sql_reset(app, self.style)
 
-        if options.get('interactive'):
-            confirm = raw_input("""
+        for connection_name, sql_list in sql_dict.items():
+            if options.get('interactive'):
+                if connection_name == _default:
+                    dbname = settings.DATABASE_NAME
+                else:
+                    dbname = settings.OTHER_DATABASES[connection_name][DATABASE_NAME]
+                confirm = raw_input("""
 You have requested a database reset.
 This will IRREVERSIBLY DESTROY any data for
 the "%s" application in the database "%s".
 Are you sure you want to do this?
 
-Type 'yes' to continue, or 'no' to cancel: """ % (app_name, settings.DATABASE_NAME))
-        else:
-            confirm = 'yes'
+Type 'yes' to continue, or 'no' to cancel: """ % (app_name, dbname))
+            else:
+                confirm = 'yes'
 
-        if confirm == 'yes':
-            try:
-                cursor = connection.cursor()
-                for sql in sql_list:
-                    cursor.execute(sql)
-            except Exception, e:
-                transaction.rollback_unless_managed()
-                raise CommandError("""Error: %s couldn't be reset. Possible reasons:
+            if confirm == 'yes':
+                try:
+                    cursor = connections[connection_name].connection.cursor()
+                    for sql in sql_list:
+                        cursor.execute(sql)
+                except Exception, e:
+                    transaction.rollback_unless_managed()
+                    raise CommandError("""Error: %s couldn't be reset. Possible reasons:
   * The database isn't running or isn't configured correctly.
   * At least one of the database tables doesn't exist.
   * The SQL was invalid.
 Hint: Look at the output of 'django-admin.py sqlreset %s'. That's the SQL this command wasn't able to run.
 The full error: %s""" % (app_name, app_name, e))
-            transaction.commit_unless_managed()
-        else:
-            print "Reset cancelled."
+                transaction.commit_unless_managed()
+            else:
+                print "Reset cancelled."
=== django/core/management/commands/syncdb.py
==================================================================
--- django/core/management/commands/syncdb.py	(/mirror/django/trunk)	(revision 5420)

+++ django/core/management/commands/syncdb.py	(/local/django/mymultidb)	(revision 5420)

@@ -19,7 +19,7 @@

     help = "Create the database tables for all apps in INSTALLED_APPS whose tables haven't already been created."
 
     def handle_noargs(self, **options):
-        from django.db import connection, transaction, models
+        from django.db import model_connection_name, transaction, models, connections
         from django.conf import settings
         from django.core.management.sql import table_list, installed_models, sql_model_create, sql_for_pending_references, many_to_many_sql_for_model, custom_sql_for_model, sql_indexes_for_model, emit_post_sync_signal
 
@@ -37,16 +37,10 @@

                 if not exc.args[0].startswith('No module named management'):
                     raise
 
-        cursor = connection.cursor()
-
-        if connection.features.uses_case_insensitive_names:
-            table_name_converter = lambda x: x.upper()
-        else:
-            table_name_converter = lambda x: x
         # Get a list of all existing database tables, so we know what needs to
         # be added.
-        tables = [table_name_converter(name) for name in table_list()]
-
+        tables = table_list()
+        
         # Get a list of already installed *models* so that references work right.
         seen_models = installed_models(tables)
         created_models = set()
@@ -59,8 +53,15 @@

             for model in model_list:
                 # Create the model's database table, if it doesn't already exist.
                 if verbosity >= 2:
-                    print "Processing %s.%s model" % (app_name, model._meta.object_name)
-                if table_name_converter(model._meta.db_table) in tables:
+                    print "Processing %s.%s model (%s))" % (app_name, model._meta.object_name, model_connection_name(model))
+                connection = model._default_manager.db.connection
+                cursor = connection.cursor()
+                if connection.features.uses_case_insensitive_names:
+                    table_name_converter = lambda x: x.upper()
+                else:
+                    table_name_converter = lambda x: x
+                ctables = [table_name_converter(name) for name in tables]
+                if table_name_converter(model._meta.db_table) in ctables:
                     continue
                 sql, references = sql_model_create(model, self.style, seen_models)
                 seen_models.add(model)
@@ -83,6 +84,8 @@

             model_list = models.get_models(app)
             for model in model_list:
                 if model in created_models:
+                    connection = model._default_manager.db.connection
+                    cursor = connection.cursor()
                     sql = many_to_many_sql_for_model(model, self.style)
                     if sql:
                         if verbosity >= 2:
@@ -102,6 +105,8 @@

             app_name = app.__name__.split('.')[-2]
             for model in models.get_models(app):
                 if model in created_models:
+                    connection = model._default_manager.db.connection
+                    cursor = connection.cursor()
                     custom_sql = custom_sql_for_model(model)
                     if custom_sql:
                         if verbosity >= 1:
@@ -121,6 +126,8 @@

             app_name = app.__name__.split('.')[-2]
             for model in models.get_models(app):
                 if model in created_models:
+                    connection = model._default_manager.db.connection
+                    cursor = connection.cursor()
                     index_sql = sql_indexes_for_model(model, self.style)
                     if index_sql:
                         if verbosity >= 1:
=== django/core/management/validation.py
==================================================================
--- django/core/management/validation.py	(/mirror/django/trunk)	(revision 5420)

+++ django/core/management/validation.py	(/local/django/mymultidb)	(revision 5420)

@@ -19,7 +19,7 @@

     Returns number of errors.
     """
     from django.conf import settings
-    from django.db import models, connection
+    from django.db import connections, models, model_connection_name
     from django.db.models.loading import get_app_errors
     from django.db.models.fields.related import RelatedObject
 
@@ -30,6 +30,8 @@

 
     for cls in models.get_models(app):
         opts = cls._meta
+        connection_name = model_connection_name(cls)
+        connection = connections[connection_name].connection
 
         # Do field-specific validation.
         for f in opts.local_fields:
@@ -74,6 +76,12 @@

             if f.rel:
                 if f.rel.to not in models.get_models():
                     e.add(opts, "'%s' has relation with model %s, which has not been installed" % (f.name, f.rel.to))
+
+#MULTIDB TODO: Fix this to allow relations that span databases by splitting querys up
+                rel_connection = model_connection_name(f.rel.to)
+                if rel_connection != connection_name:
+                    e.add(opts, "'%s' is configured to use connection '%s' but has relation with '%s', which is configured to use connection '%s'" % (cls.__name__, connection_name, f.rel.to.__name__, rel_connection))
+
                 # it is a string and we could not find the model it refers to
                 # so skip the next section
                 if isinstance(f.rel.to, (str, unicode)):
=== django/core/management/sql.py
==================================================================
--- django/core/management/sql.py	(/mirror/django/trunk)	(revision 5420)

+++ django/core/management/sql.py	(/local/django/mymultidb)	(revision 5420)

@@ -8,11 +8,21 @@

     from sets import Set as set   # Python 2.3 fallback
 
 def table_list():
-    "Returns a list of all table names that exist in the database."
-    from django.db import connection, get_introspection_module
-    cursor = connection.cursor()
-    return get_introspection_module().get_table_list(cursor)
+    """Returns a list of all table names that exist in the database."""
+    from django.db import connections
+    table_list = []
+    for conn in connections:
+        table_list.extend(table_list_conn(conn))
+    return table_list
 
+def table_list_conn(conn):
+    from django.db import connections
+    try:
+        cursor = connections[conn].connection.cursor()
+        return connections[conn].get_introspection_module().get_table_list(cursor)
+    except:
+        return []
+
 def django_table_list(only_existing=False):
     """
     Returns a list of all table names that have associated Django models and
@@ -32,18 +42,34 @@

         tables = [t for t in tables if t in existing]
     return tables
 
+def django_table_list_conn(conn, only_existing=False):
+    from django.db import models
+    from django.db import model_connection_name
+    tables = []
+    for app in models.get_apps():
+        for model in models.get_models(app):
+            if model_connection_name(model)==conn:
+                tables.append(model._meta.db_table)
+                tables.extend([f.m2m_db_table() for f in model._meta.many_to_many])
+    if only_existing:
+        existing = table_list_conn(conn)
+        tables = [t for t in tables if t in existing]
+    return tables
+
 def installed_models(table_list):
     "Returns a set of all models that are installed, given a list of existing table names."
-    from django.db import connection, models
+    from django.db import models
     all_models = []
     for app in models.get_apps():
         for model in models.get_models(app):
-            all_models.append(model)
-    if connection.features.uses_case_insensitive_names:
-        converter = lambda x: x.upper()
-    else:
-        converter = lambda x: x
-    return set([m for m in all_models if converter(m._meta.db_table) in map(converter, table_list)])
+            connection = model._default_manager.db.connection
+            if connection.features.uses_case_insensitive_names:
+                converter = lambda x: x.upper()
+            else:
+                converter = lambda x: x
+            if converter(model._meta.db_table) in map(converter, table_list):
+                all_models.append(converter(model))
+    return set(all_models)
 
 def sequence_list():
     "Returns a list of information about all DB sequences for all models in all apps."
@@ -66,7 +92,7 @@

 
 def sql_create(app, style):
     "Returns a list of the CREATE TABLE SQL statements for the given app."
-    from django.db import models
+    from django.db import models, model_connection_name, _default
     from django.conf import settings
 
     if settings.DATABASE_ENGINE == 'dummy':
@@ -81,71 +107,79 @@

     # generate invalid SQL (leaving models out of known_models is harmless, so
     # we can be conservative).
     app_models = models.get_models(app)
-    final_output = []
     known_models = set([model for model in installed_models(table_list()) if model not in app_models])
     pending_references = {}
 
+    connection_output = {}
+    
     for model in app_models:
+        connection_name = model_connection_name(model)
+        f_output = connection_output.setdefault(connection_name, [])
         output, references = sql_model_create(model, style, known_models)
-        final_output.extend(output)
+        f_output.extend(output)
         for refto, refs in references.items():
             pending_references.setdefault(refto, []).extend(refs)
             if refto in known_models:
-                final_output.extend(sql_for_pending_references(refto, style, pending_references))
-        final_output.extend(sql_for_pending_references(model, style, pending_references))
+                f_output.extend(sql_for_pending_references(refto, style, pending_references))
+        f_output.extend(sql_for_pending_references(model, style, pending_references))
         # Keep track of the fact that we've created the table for this model.
         known_models.add(model)
 
     # Create the many-to-many join tables.
     for model in app_models:
-        final_output.extend(many_to_many_sql_for_model(model, style))
+        connection_name = model_connection_name(model)
+        f_output = connection_output.setdefault(connection_name, [])
+        f_output.extend(many_to_many_sql_for_model(model,style))
 
+
     # Handle references to tables that are from other apps
     # but don't exist physically.
     not_installed_models = set(pending_references.keys())
     if not_installed_models:
+        f_output = connection_output.setdefault(_default, [])
         alter_sql = []
         for model in not_installed_models:
             alter_sql.extend(['-- ' + sql for sql in
                 sql_for_pending_references(model, style, pending_references)])
         if alter_sql:
-            final_output.append('-- The following references should be added but depend on non-existent tables:')
-            final_output.extend(alter_sql)
+            f_output.append('-- The following references should be added but depend on non-existent tables:')
+            f_output.extend(alter_sql)
 
-    return final_output
+    return connection_output
 
 def sql_delete(app, style):
     "Returns a list of the DROP TABLE SQL statements for the given app."
-    from django.db import connection, models, get_introspection_module
+    from django.db import models, model_connection_name
     from django.db.backends.util import truncate_name
     from django.contrib.contenttypes import generic
-    introspection = get_introspection_module()
 
-    # This should work even if a connection isn't available
-    try:
-        cursor = connection.cursor()
-    except:
-        cursor = None
+    connection_output = {}
 
-    # Figure out which tables already exist
-    if cursor:
-        table_names = introspection.get_table_list(cursor)
-    else:
-        table_names = []
-    if connection.features.uses_case_insensitive_names:
-        table_name_converter = lambda x: x.upper()
-    else:
-        table_name_converter = lambda x: x
-
-    output = []
-    qn = connection.ops.quote_name
-
     # Output DROP TABLE statements for standard application tables.
     to_delete = set()
 
     references_to_delete = {}
     app_models = models.get_models(app)
     for model in app_models:
+        connection = model._default_manager.db.connection
+        introspection = model._default_manager.db.get_introspection_module()
+        # This should work even if a connection isn't available
+        try:
+            cursor = connection.cursor()
+        except:
+            cursor = None
+        # Figure out which tables already exist
+        if cursor:
+            table_names = introspection.get_table_list(cursor)
+        else:
+            table_names = []
+        if connection.features.uses_case_insensitive_names:
+            table_name_converter = lambda x: x.upper()
+        else:
+            table_name_converter = lambda x: x
+
+        qn = connection.ops.quote_name
+
         if cursor and table_name_converter(model._meta.db_table) in table_names:
             # The table exists, so it needs to be dropped
             opts = model._meta
@@ -156,6 +190,29 @@

             to_delete.add(model)
 
     for model in app_models:
+        connection = model._default_manager.db.connection
+        connection_name = model_connection_name(model)
+        introspection = model._default_manager.db.get_introspection_module()
+
+        output = connection_output.setdefault(connection_name, [])
+
+        # This should work even if a connection isn't available
+        try:
+            cursor = connection.cursor()
+        except:
+            cursor = None
+        # Figure out which tables already exist
+        if cursor:
+            table_names = introspection.get_table_list(cursor)
+        else:
+            table_names = []
+        if connection.features.uses_case_insensitive_names:
+            table_name_converter = str.upper
+        else:
+            table_name_converter = lambda x: x
+
+        qn = connection.ops.quote_name
+
         if cursor and table_name_converter(model._meta.db_table) in table_names:
             # Drop the table now
             output.append('%s %s;' % (style.SQL_KEYWORD('DROP TABLE'),
@@ -180,6 +237,29 @@

 
     # Output DROP TABLE statements for many-to-many tables.
     for model in app_models:
+        connection = model._default_manager.db.connection
+        connection_name = model_connection_name(model)
+        introspection = model._default_manager.db.get_introspection_module()
+
+        output = connection_output.setdefault(connection_name, [])
+
+        # This should work even if a connection isn't available
+        try:
+            cursor = connection.cursor()
+        except:
+            cursor = None
+        # Figure out which tables already exist
+        if cursor:
+            table_names = introspection.get_table_list(cursor)
+        else:
+            table_names = []
+        if connection.features.uses_case_insensitive_names:
+            table_name_converter = str.upper
+        else:
+            table_name_converter = lambda x: x
+
+        qn = connection.ops.quote_name
+
         opts = model._meta
         for f in opts.local_many_to_many:
             if isinstance(f.rel, generic.GenericRel):
@@ -195,71 +275,106 @@

 
     # Close database connection explicitly, in case this output is being piped
     # directly into a database client, to avoid locking issues.
-    if cursor:
-        cursor.close()
-        connection.close()
+    for model in app_models:
+        connection = model._default_manager.db.connection
+        try:
+            cursor = connection.cursor()
+        except:
+            cursor = None
+        if cursor:
+            cursor.close()
+            connection.close()
 
-    return output[::-1] # Reverse it, to deal with table dependencies.
+    return connection_output
 
 def sql_reset(app, style):
     "Returns a list of the DROP TABLE SQL, then the CREATE TABLE SQL, for the given module."
-    return sql_delete(app, style) + sql_all(app, style)
+    connection_output = {}
+    sql_dict = sql_delete(app, style)
+    for connection_name, sql_list in sql_dict.items():
+        output = connection_output.setdefault(connection_name, [])
+        output.extend(sql_list)
+    sql_dict = sql_all(app, style)
+    for connection_name, sql_list in sql_dict.items():
+        output = connection_output.setdefault(connection_name, [])
+        output.extend(sql_list)
+    return connection_output
 
-def sql_flush(style, only_django=False):
+def sql_flush(connection, style, only_django=False):
     """
     Returns a list of the SQL statements used to flush the database.
     
     If only_django is True, then only table names that have associated Django
     models and are in INSTALLED_APPS will be included.
     """
-    from django.db import connection
+    from django.db import connections
     if only_django:
-        tables = django_table_list()
+        tables = django_table_list_conn(connection)
     else:
-        tables = table_list()
-    statements = connection.ops.sql_flush(style, tables, sequence_list())
-    return statements
+        tables = table_list_conn(connection)
+    return [f for f in connections[connection].connection.ops.sql_flush(style, tables, sequence_list())]
 
 def sql_custom(app):
     "Returns a list of the custom table modifying SQL statements for the given app."
     from django.db.models import get_models
-    output = []
+    from django.db import model_connection_name
+    connection_output = {}
 
     app_models = get_models(app)
     app_dir = os.path.normpath(os.path.join(os.path.dirname(app.__file__), 'sql'))
 
     for model in app_models:
-        output.extend(custom_sql_for_model(model))
+        connection_name = model_connection_name(model)
+        output = connection_output.setdefault(connection_name, [])
+        output.extend(map(str, custom_sql_for_model(model)))
+    return connection_output
 
-    return output
-
 def sql_indexes(app, style):
     "Returns a list of the CREATE INDEX SQL statements for all models in the given app."
-    from django.db import models
-    output = []
-    for model in models.get_models(app):
-        output.extend(sql_indexes_for_model(model, style))
-    return output
+    from django.db import model_connection_name
+    from django.db.models import get_models
+    connection_output = {}
+    for model in get_models(app):
+        opts = model._meta
+        connection_name = model_connection_name(model)
+        output = connection_output.setdefault(connection_name, [])
+        output.extend(map(str, sql_indexes_for_model(model, style)))
+    return connection_output
 
 def sql_all(app, style):
     "Returns a list of CREATE TABLE SQL, initial-data inserts, and CREATE INDEX SQL for the given module."
-    return sql_create(app, style) + sql_custom(app) + sql_indexes(app, style)
+    connection_output = {}
+    sql_dict = sql_create(app, style)
+    for connection_name, sql_list in sql_dict.items():
+        output = connection_output.setdefault(connection_name, [])
+        output.extend(sql_list)
+    sql_dict = sql_custom(app)
+    for connection_name, sql_list in sql_dict.items():
+        output = connection_output.setdefault(connection_name, [])
+        output.extend(sql_list)
+    sql_dict = sql_indexes(app, style)
+    for connection_name, sql_list in sql_dict.items():
+        output = connection_output.setdefault(connection_name, [])
+        output.extend(sql_list)
+    return connection_output
 
 def sql_model_create(model, style, known_models=set()):
     """
     Returns the SQL required to create a single model, as a tuple of:
         (list_of_sql, pending_references_dict)
     """
-    from django.db import connection, models
+    from django.db import models, model_connection_name, connections
 
     opts = model._meta
     final_output = []
     table_output = []
     pending_references = {}
+    connection_name = model_connection_name(model)
+    connection = connections[connection_name].connection
     qn = connection.ops.quote_name
     inline_references = connection.features.inline_fk_references
     for f in opts.local_fields:
-        col_type = f.db_type()
+        col_type = f.db_type(connection_name)
         tablespace = f.db_tablespace or opts.db_tablespace
         if col_type is None:
             # Skip ManyToManyFields, because they're not represented as
@@ -291,7 +406,7 @@

         table_output.append(' '.join(field_output))
     if opts.order_with_respect_to:
         table_output.append(style.SQL_FIELD(qn('_order')) + ' ' + \
-            style.SQL_COLTYPE(models.IntegerField().db_type()) + ' ' + \
+            style.SQL_COLTYPE(models.IntegerField().db_type(connection_name)) + ' ' + \
             style.SQL_KEYWORD('NULL'))
     for field_constraints in opts.unique_together:
         table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \
@@ -320,10 +435,16 @@

     """
     Returns any ALTER TABLE statements to add constraints after the fact.
     """
-    from django.db import connection
+    from django.db import connections, model_connection_name
     from django.db.backends.util import truncate_name
 
+    connection_name = model_connection_name(model)
+    connection = connections[connection_name].connection
     qn = connection.ops.quote_name
+##    if hasattr(connection.ops, 'max_constraint_length'):
+##        mnl = connection.ops.max_constraint_length
+##    else:
+##        mnl = connection.ops.max_name_length
     final_output = []
     if connection.features.supports_constraints:
         opts = model._meta
@@ -345,12 +466,14 @@

     return final_output
 
 def many_to_many_sql_for_model(model, style):
-    from django.db import connection, models
+    from django.db import models, connections, model_connection_name
     from django.contrib.contenttypes import generic
     from django.db.backends.util import truncate_name
 
     opts = model._meta
     final_output = []
+    connection_name = model_connection_name(model)
+    connection = connections[connection_name].connection
     qn = connection.ops.quote_name
     inline_references = connection.features.inline_fk_references
     for f in opts.local_many_to_many:
@@ -364,21 +487,21 @@

                 style.SQL_TABLE(qn(f.m2m_db_table())) + ' (']
             table_output.append('    %s %s %s%s,' %
                 (style.SQL_FIELD(qn('id')),
-                style.SQL_COLTYPE(models.AutoField(primary_key=True).db_type()),
+                style.SQL_COLTYPE(models.AutoField(primary_key=True).db_type(connection_name)),
                 style.SQL_KEYWORD('NOT NULL PRIMARY KEY'),
                 tablespace_sql))
             if inline_references:
                 deferred = []
                 table_output.append('    %s %s %s %s (%s)%s,' %
                     (style.SQL_FIELD(qn(f.m2m_column_name())),
-                    style.SQL_COLTYPE(models.ForeignKey(model).db_type()),
+                    style.SQL_COLTYPE(models.ForeignKey(model).db_type(connection_name)),
                     style.SQL_KEYWORD('NOT NULL REFERENCES'),
                     style.SQL_TABLE(qn(opts.db_table)),
                     style.SQL_FIELD(qn(opts.pk.column)),
                     connection.ops.deferrable_sql()))
                 table_output.append('    %s %s %s %s (%s)%s,' %
                     (style.SQL_FIELD(qn(f.m2m_reverse_name())),
-                    style.SQL_COLTYPE(models.ForeignKey(f.rel.to).db_type()),
+                    style.SQL_COLTYPE(models.ForeignKey(f.rel.to).db_type(connection_name)),
                     style.SQL_KEYWORD('NOT NULL REFERENCES'),
                     style.SQL_TABLE(qn(f.rel.to._meta.db_table)),
                     style.SQL_FIELD(qn(f.rel.to._meta.pk.column)),
@@ -386,11 +509,11 @@

             else:
                 table_output.append('    %s %s %s,' %
                     (style.SQL_FIELD(qn(f.m2m_column_name())),
-                    style.SQL_COLTYPE(models.ForeignKey(model).db_type()),
+                    style.SQL_COLTYPE(models.ForeignKey(model).db_type(connection_name)),
                     style.SQL_KEYWORD('NOT NULL')))
                 table_output.append('    %s %s %s,' %
                     (style.SQL_FIELD(qn(f.m2m_reverse_name())),
-                    style.SQL_COLTYPE(models.ForeignKey(f.rel.to).db_type()),
+                    style.SQL_COLTYPE(models.ForeignKey(f.rel.to).db_type(connection_name)),
                     style.SQL_KEYWORD('NOT NULL')))
                 deferred = [
                     (f.m2m_db_table(), f.m2m_column_name(), opts.db_table,
@@ -456,9 +579,9 @@

 
 def sql_indexes_for_model(model, style):
     "Returns the CREATE INDEX SQL statements for a single model"
-    from django.db import connection
     output = []
 
+    connection = model._default_manager.db.connection
     qn = connection.ops.quote_name
     for f in model._meta.local_fields:
         if f.db_index and not ((f.primary_key or f.unique) and connection.features.autoindexes_primary_keys):
@@ -489,3 +612,24 @@

         dispatcher.send(signal=models.signals.post_syncdb, sender=app,
             app=app, created_models=created_models,
             verbosity=verbosity, interactive=interactive)
+
+def sql_collate(connection_output, reverse=False):
+    from django.db import _default
+    final_output = []
+    if len(connection_output.keys()) == 1:
+        # all for the default connection
+        for statements in connection_output.values():
+            final_output.extend(statements)
+            if reverse:
+                final_output.reverse()
+    else:
+        for connection_name, statements in connection_output.items():
+            if not statements:
+                continue
+            final_output.append(' -- The following statements are for connection: %s' % connection_name)
+            if reverse:
+                statements.reverse()
+            final_output.extend(statements)
+            final_output.append(' -- END statements for %s\n' %
+                                connection_name)
+    return '\n'.join(map(str, final_output))
=== django/core/context_processors.py
==================================================================
--- django/core/context_processors.py	(/mirror/django/trunk)	(revision 5420)

+++ django/core/context_processors.py	(/local/django/mymultidb)	(revision 5420)

@@ -33,8 +33,11 @@

     context_extras = {}
     if settings.DEBUG and request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS:
         context_extras['debug'] = True
-        from django.db import connection
-        context_extras['sql_queries'] = connection.queries
+        from django.db import connections
+        queries = []
+        for conn in connections:
+            queries.extend(connections[conn].connection.queries)
+        context_extras['sql_queries'] = queries
     return context_extras
 
 def i18n(request):
=== django/contrib/contenttypes/generic.py
==================================================================
--- django/contrib/contenttypes/generic.py	(/mirror/django/trunk)	(revision 5420)

+++ django/contrib/contenttypes/generic.py	(/local/django/mymultidb)	(revision 5420)

@@ -4,7 +4,6 @@

 
 from django import oldforms
 from django.core.exceptions import ObjectDoesNotExist
-from django.db import connection
 from django.db.models import signals
 from django.db.models.fields.related import RelatedField, Field, ManyToManyRel
 from django.db.models.loading import get_model
@@ -154,7 +153,7 @@

     def get_internal_type(self):
         return "ManyToManyField"
 
-    def db_type(self):
+    def db_type(self, connection):
         # Since we're simulating a ManyToManyField, in effect, best return the
         # same db_type as well.
         return None
@@ -184,7 +183,7 @@

         superclass = rel_model._default_manager.__class__
         RelatedManager = create_generic_related_manager(superclass)
 
-        qn = connection.ops.quote_name
+        qn = rel_model._default_manager.db.connection.ops.quote_name
 
         manager = RelatedManager(
             model = rel_model,
=== django/contrib/auth/backends.py
==================================================================
--- django/contrib/auth/backends.py	(/mirror/django/trunk)	(revision 5420)

+++ django/contrib/auth/backends.py	(/local/django/mymultidb)	(revision 5420)

@@ -1,4 +1,3 @@

-from django.db import connection
 from django.contrib.auth.models import User
 
 try: 
@@ -23,6 +22,7 @@

     def get_group_permissions(self, user_obj):
         "Returns a list of permission strings that this user has through his/her groups."
         if not hasattr(user_obj, '_group_perm_cache'):
+            connection = User._default_manager.db.connection
             cursor = connection.cursor()
             # The SQL below works out to the following, after DB quoting:
             # cursor.execute("""
=== tests/modeltests/multiple_databases	(new directory)
==================================================================
=== tests/modeltests/multiple_databases/__init__.py
==================================================================
--- tests/modeltests/multiple_databases/__init__.py	(/mirror/django/trunk)	(revision 5420)

+++ tests/modeltests/multiple_databases/__init__.py	(/local/django/mymultidb)	(revision 5420)

@@ -0,0 +1 @@

+pass

=== tests/modeltests/multiple_databases/models.py
==================================================================
--- tests/modeltests/multiple_databases/models.py	(/mirror/django/trunk)	(revision 5420)

+++ tests/modeltests/multiple_databases/models.py	(/local/django/mymultidb)	(revision 5420)

@@ -0,0 +1,221 @@

+"""
+XXX. Using multiple database connections
+
+Django normally uses only a single database connection. However,
+support is available for using any number of different, named
+connections. Multiple database support is entirely optional and has
+no impact on your application if you don't use it.
+
+Named connections are defined in your settings module. Create a
+`OTHER_DATABASES` variable that is a dict, mapping connection names to their
+particulars. The particulars are defined in a dict with the same keys
+as the variable names as are used to define the default connection, with one
+addition: MODELS.
+
+The MODELS item in an OTHER_DATABASES entry is a list of the apps and models
+that will use that connection. 
+
+Access to named connections is through `django.db.connections`, which
+behaves like a dict: you access connections by name. Connections are
+established lazily, when accessed.  `django.db.connections[database]`
+holds a `ConnectionInfo` instance, with the attributes:
+`DatabaseError`, `backend`, `get_introspection_module`,
+`get_creation_module`, and `runshell`.
+
+To access a model's connection, use its manager. The connection is available
+at `model._default_manager.db.connection`. To find the backend or other
+connection metadata, use `model._meta.db` to access the full ConnectionInfo
+with connection metadata.
+"""
+
+from django.db import models
+
+class Artist(models.Model):
+    name = models.CharField(max_length=100)
+    alive = models.BooleanField(default=True)
+    
+    def __str__(self):
+        return self.name
+
+    
+class Opus(models.Model):
+    artist = models.ForeignKey(Artist)
+    name = models.CharField(max_length=100)
+    year = models.IntegerField()
+    
+    def __str__(self):
+        return "%s (%s)" % (self.name, self.year)
+
+
+class Widget(models.Model):
+    code = models.CharField(max_length=10, unique=True)
+    weight = models.IntegerField()
+
+    def __str__(self):
+        return self.code
+
+
+class DooHickey(models.Model):
+    name = models.CharField(max_length=50)
+    widgets = models.ManyToManyField(Widget, related_name='doohickeys')
+    
+    def __str__(self):
+        return self.name
+
+
+class Vehicle(models.Model):
+    make = models.CharField(max_length=20)
+    model = models.CharField(max_length=20)
+    year = models.IntegerField()
+
+    def __str__(self):
+        return "%d %s %s" % (self.year, self.make, self.model)
+
+
+__test__ = {'API_TESTS': """
+
+# See what connections are defined. django.db.connections acts like a dict.
+>>> from django.db import connection, connections, _default, model_connection_name
+>>> from django.conf import settings
+
+# Connections are referenced by name
+>>> connections['_a']
+Connection: ...
+>>> connections['_b']
+Connection: ...
+
+# Let's see what connections are available. The default connection is always
+# included in connections as well, and may be accessed as connections[_default].
+
+>>> connection_names = connections.keys()
+>>> connection_names.sort()
+>>> connection_names
+[<default>, '_a', '_b']
+    
+# Invalid connection names raise ImproperlyConfigured
+
+>>> connections['bad']
+Traceback (most recent call last):
+ ...
+ImproperlyConfigured: No database connection 'bad' has been configured
+
+# The model_connection_name() function will tell you the name of the
+# connection that a model is configured to use.
+
+>>> model_connection_name(Artist)
+'_a'
+>>> model_connection_name(Widget)
+'_b'
+>>> model_connection_name(Vehicle) is _default
+True
+>>> a = Artist(name="Paul Klee", alive=False)
+>>> a.save()
+>>> w = Widget(code='100x2r', weight=1000)
+>>> w.save()
+>>> v = Vehicle(make='Chevy', model='Camaro', year='1966')
+>>> v.save()
+>>> artists = Artist.objects.all()
+>>> list(artists)
+[<Artist: Paul Klee>]
+
+# Models can access their connections through the db property of their
+# default manager.
+
+>>> paul = _[0]
+>>> Artist.objects.db
+Connection: ... (ENGINE=... NAME=...)
+>>> paul._default_manager.db
+Connection: ... (ENGINE=... NAME=...)
+
+# When transactions are not managed, model save will commit only
+# for the model's connection.
+
+>>> from django.db import transaction
+>>> transaction.enter_transaction_management()
+>>> transaction.managed(False)
+>>> a = Artist(name="Joan Miro", alive=False)
+>>> w = Widget(code="99rbln", weight=1)
+>>> a.save()
+
+# Only connection '_a' is committed, so if we rollback
+# all connections we'll forget the new Widget.
+
+>>> transaction.rollback()
+>>> list(Artist.objects.all())
+[<Artist: Paul Klee>, <Artist: Joan Miro>]
+>>> list(Widget.objects.all())
+[<Widget: 100x2r>]
+
+# Managed transaction state applies across all connections.
+
+>>> transaction.managed(True)
+
+# When managed, just as when using a single connection, updates are
+# not committed until a commit is issued.
+
+>>> a = Artist(name="Pablo Picasso", alive=False)
+>>> a.save()
+>>> w = Widget(code="99rbln", weight=1)
+>>> w.save()
+>>> v = Vehicle(make='Pontiac', model='Fiero', year='1987')
+>>> v.save()
+
+# The connections argument may be passed to commit, rollback, and the
+# commit_on_success decorator as a keyword argument, as the first (for
+# commit and rollback) or second (for the decorator) positional
+# argument. It may be passed as a ConnectionInfo object, a connection
+# (DatabaseWrapper) object, a connection name, or a list or dict of
+# ConnectionInfo objects, connection objects, or connection names. If a
+# dict is passed, the keys are ignored and the values used as the list
+# of connections to commit, rollback, etc.
+
+>>> transaction.commit(connections['_b'])
+>>> transaction.commit('_b')
+>>> transaction.commit(connections='_b')
+>>> transaction.commit(connections=['_b'])
+>>> transaction.commit(['_a', '_b'])
+>>> transaction.commit(connections)
+
+# When the connections argument is omitted entirely, the transaction
+# command applies to all connections. Here we have committed
+# connections 'django_test_db_a' and 'django_test_db_b', but not the
+# default connection, so the new vehicle is lost on rollback.
+
+>>> transaction.rollback()
+>>> list(Artist.objects.all())
+[<Artist: Paul Klee>, <Artist: Joan Miro>, <Artist: Pablo Picasso>]
+>>> list(Widget.objects.all())
+[<Widget: 100x2r>, <Widget: 99rbln>]
+>>> list(Vehicle.objects.all())
+[<Vehicle: 1966 Chevy Camaro>]
+>>> transaction.rollback()
+>>> transaction.managed(False)
+>>> transaction.leave_transaction_management()
+
+# Of course, relations and all other normal database operations work
+# with models that use named connections just the same as with models
+# that use the default connection. The only caveat is that you can't
+# use a relation between two models that are stored in different
+# databases. Note that that doesn't mean that two models using
+# different connection *names* can't be related; only that in the the
+# context in which they are used, if you use the relation, the
+# connections named by the two models must resolve to the same
+# database.
+
+>>> a = Artist.objects.get(name="Paul Klee")
+>>> list(a.opus_set.all())
+[]
+>>> a.opus_set.create(name="Magic Garden", year="1926")
+<Opus: Magic Garden (1926)>
+>>> list(a.opus_set.all())
+[<Opus: Magic Garden (1926)>]
+>>> d = DooHickey(name='Thing')
+>>> d.save()
+>>> d.widgets.create(code='d101', weight=92)
+<Widget: d101>
+>>> list(d.widgets.all())
+[<Widget: d101>]
+>>> w = Widget.objects.get(code='d101')
+>>> list(w.doohickeys.all())
+[<DooHickey: Thing>]
+"""}

Property changes on: tests/modeltests/multiple_databases
___________________________________________________________________
Name: svn:ignore
 +*.pyc
 +

=== tests/regressiontests/manager_db	(new directory)
==================================================================
=== tests/regressiontests/manager_db/__init__.py
==================================================================
=== tests/regressiontests/manager_db/tests.py
==================================================================
--- tests/regressiontests/manager_db/tests.py	(/mirror/django/trunk)	(revision 5420)

+++ tests/regressiontests/manager_db/tests.py	(/local/django/mymultidb)	(revision 5420)

@@ -0,0 +1,17 @@

+import unittest
+from regressiontests.manager_db.models import Insect
+
+class TestManagerDBAccess(unittest.TestCase):
+
+    def test_db_property(self):
+        m = Insect.objects
+        db = Insect.objects.db
+        assert db
+        assert db.connection
+        assert db.connection.cursor
+        assert db.backend
+        assert db.connection.ops.quote_name
+        assert db.get_creation_module
+
+if __name__ == '__main__':
+    unittest.main()
=== tests/regressiontests/manager_db/models.py
==================================================================
--- tests/regressiontests/manager_db/models.py	(/mirror/django/trunk)	(revision 5420)

+++ tests/regressiontests/manager_db/models.py	(/local/django/mymultidb)	(revision 5420)

@@ -0,0 +1,5 @@

+from django.db import models
+
+class Insect(models.Model):
+    common_name = models.CharField(max_length=64)
+    latin_name = models.CharField(max_length=128)

Property changes on: tests/regressiontests/manager_db
___________________________________________________________________
Name: svn:ignore
 +*.pyc
 +

=== tests/regressiontests/thread_isolation	(new directory)
==================================================================
=== tests/regressiontests/thread_isolation/__init__.py
==================================================================
=== tests/regressiontests/thread_isolation/tests.py
==================================================================
--- tests/regressiontests/thread_isolation/tests.py	(/mirror/django/trunk)	(revision 5420)

+++ tests/regressiontests/thread_isolation/tests.py	(/local/django/mymultidb)	(revision 5420)

@@ -0,0 +1,251 @@

+# tests that db settings can be different in different threads
+#
+#
+#    What's going on here:
+#
+#    Simulating multiple web requests in a threaded environment, one in
+#    which settings are different for each request. So we replace
+#    django.conf.settings with a thread local, with different
+#    configurations in each thread, and then fire off three
+#    simultaneous requests (using a condition to sync them up), and
+#    test that each thread sees its own settings and the models in each
+#    thread attempt to connect to the correct database as per their
+#    settings.
+#
+
+
+import copy
+import os
+import sys
+import threading
+import unittest
+from thread import get_ident
+
+from django.conf import settings, UserSettingsHolder
+from django.core.handlers.wsgi import WSGIHandler
+from django.db import model_connection_name, _default, connection, connections
+from regressiontests.request_isolation.tests import MockHandler
+from regressiontests.thread_isolation.models import *
+
+try:
+    # Only exists in Python 2.4+
+    from threading import local
+except ImportError:
+    # Import copy of _thread_local.py from Python 2.4
+    from django.utils._threading_local import local
+
+# helpers
+EV = threading.Event()
+
+class LocalSettings:
+    """Settings holder that allows thread-local overrides of defaults.
+    """
+    def __init__(self, defaults):
+        self._defaults = defaults
+        self._local = local()
+
+    def __getattr__(self, attr):
+        if attr in ('_defaults', '_local'):
+            return self.__dict__[attr]
+        _local = self.__dict__['_local']
+        _defaults = self.__dict__['_defaults']
+        debug("LS get %s (%s)", attr, hasattr(_local, attr))
+        if not hasattr(_local, attr):
+            # Make sure everything we return is the local version; this
+            # avoids sets to deep datastructures overwriting the defaults
+            setattr(_local, attr, copy.deepcopy(getattr(_defaults, attr)))
+        return getattr(_local, attr)
+
+    def __setattr__(self, attr, val):
+        if attr in ('_defaults', '_local'):
+            self.__dict__[attr] = val
+        else:
+            debug("LS set local %s = %s", attr, val)
+            setattr(self.__dict__['_local'], attr, val)
+
+def thread_two(func, *arg):
+    def start():
+        # from django.conf import settings
+        settings.OTHER_DATABASES['_b']['MODELS'] = []
+
+        debug("t2 ODB: %s", settings.OTHER_DATABASES)
+        debug("t2 waiting")
+        EV.wait(2.0)
+        func(*arg)
+        debug("t2 complete")
+    t2 = threading.Thread(target=start)
+    t2.start()
+    return t2
+
+def thread_three(func, *arg):
+    def start():
+        # from django.conf import settings            
+        settings.OTHER_DATABASES['_b']['MODELS'] = ['ti.MY']
+        settings.OTHER_DATABASES['_b'], \
+            settings.OTHER_DATABASES['_a'] = \
+            settings.OTHER_DATABASES['_a'], \
+            settings.OTHER_DATABASES['_b']
+
+        settings.DATABASE_NAME = \
+            settings.OTHER_DATABASES['_a']['DATABASE_NAME']
+
+        debug("t3 ODB: %s", settings.OTHER_DATABASES)
+        debug("3 %s: start: default: %s", get_ident(), settings.DATABASE_NAME)
+        debug("3 %s: start: conn: %s", get_ident(),
+              connection.settings.DATABASE_NAME)
+        
+        debug("t3 waiting")
+        EV.wait(2.0)
+        func(*arg)
+        debug("t3 complete")
+    t3 = threading.Thread(target=start)
+    t3.start()
+    return t3
+
+def debug(*arg):
+    pass
+##    msg, arg = arg[0], arg[1:]
+##    print msg % arg
+
+def start_response(code, headers):
+    debug("start response: %s %s", code, headers)
+    pass
+    
+class TestThreadIsolation(unittest.TestCase):
+    # event used to synchronize threads so we can be sure they are running
+    # together
+    lock = threading.RLock()
+    errors = []
+    
+    def setUp(self):
+        debug("setup")
+        self.settings = settings._target
+        settings._target = UserSettingsHolder(copy.deepcopy(settings._target))
+        settings.OTHER_DATABASES['_a']['MODELS'] =  ['ti.MX']
+        settings.OTHER_DATABASES['_b']['MODELS'] = ['ti.MY']
+
+        # normal settings holders aren't thread-safe, so we need to substitute
+        # one that is (and so allows per-thread settings)
+        holder = settings._target
+        settings._target = LocalSettings(holder)
+
+    def teardown(self):
+        debug("teardown")
+        settings._target = self.settings
+
+    def add_thread_error(self, err):
+        self.lock.acquire()
+        try:
+            self.errors.append(err)
+        finally:
+            self.lock.release()
+
+    def thread_errors(self):
+        self.lock.acquire()
+        try:
+            return self.errors[:]
+        finally:
+            self.lock.release()
+            
+    def request_one(self, request):
+        """Start out with settings as originally configured"""
+        from django.conf import settings
+        debug("request_one: %s", settings.OTHER_DATABASES)
+
+        self.assertEqual(model_connection_name(MQ), _default)
+        self.assertEqual(model_connection_name(MX), '_a')
+        self.assertEqual(
+            MX._default_manager.db.connection.settings.DATABASE_NAME, 
+            settings.OTHER_DATABASES['_a']['DATABASE_NAME'])
+        self.assertEqual(model_connection_name(MY), '_b')
+        self.assertEqual(
+            MY._default_manager.db.connection.settings.DATABASE_NAME,
+            settings.OTHER_DATABASES['_b']['DATABASE_NAME'])
+        self.assert_(MQ._default_manager.db.connection is
+                     connections[_default].connection)
+        self.assertEqual(
+            MQ._default_manager.db.connection.settings.DATABASE_NAME,
+            settings.DATABASE_NAME)
+        self.assertEqual(connection.settings.DATABASE_NAME,
+                         settings.DATABASE_NAME)
+
+    def request_two(self, request):
+        """Between the first and second requests, settings change to assign
+        model MY to a different connection
+        """
+        # from django.conf import settings
+        debug("request_two: %s", settings.OTHER_DATABASES)
+
+        try:
+            self.assertEqual(model_connection_name(MQ), _default)
+            self.assertEqual(model_connection_name(MX), '_a')
+            self.assertEqual(
+                MX._default_manager.db.connection.settings.DATABASE_NAME,
+                settings.OTHER_DATABASES['_a']['DATABASE_NAME'])
+            self.assertEqual(model_connection_name(MY), _default)
+            self.assertEqual(
+                MY._default_manager.db.connection.settings.DATABASE_NAME,
+                settings.DATABASE_NAME)
+            self.assert_(MQ._default_manager.db.connection is
+                         connections[_default].connection)
+            self.assertEqual(
+                MQ._default_manager.db.connection.settings.DATABASE_NAME,
+                settings.DATABASE_NAME)
+            self.assertEqual(connection.settings.DATABASE_NAME,
+                             settings.DATABASE_NAME)
+        except:
+            self.add_thread_error(sys.exc_info())
+
+    def request_three(self, request):
+        """Between the 2nd and 3rd requests, the settings at the names in
+        OTHER_DATABASES have changed.
+        """
+        # from django.conf import settings
+        debug("3 %s: %s", get_ident(), settings.OTHER_DATABASES)
+        debug("3 %s: default: %s", get_ident(), settings.DATABASE_NAME)
+        debug("3 %s: conn: %s", get_ident(),
+              connection.settings.DATABASE_NAME)
+        try:
+            self.assertEqual(model_connection_name(MQ), _default)
+            self.assertEqual(model_connection_name(MX), '_b')
+            self.assertEqual(
+                MX._default_manager.db.connection.settings.DATABASE_NAME,
+                settings.OTHER_DATABASES['_b']['DATABASE_NAME'])
+            self.assertEqual(model_connection_name(MY), '_a')
+            self.assertEqual(
+                MY._default_manager.db.connection.settings.DATABASE_NAME,
+                settings.OTHER_DATABASES['_a']['DATABASE_NAME'])
+            self.assert_(MQ._default_manager.db.connection is
+                         connections[_default].connection)
+            self.assertEqual(
+                connection.settings.DATABASE_NAME,
+                settings.OTHER_DATABASES['_a']['DATABASE_NAME'])
+        except:
+            self.add_thread_error(sys.exc_info())
+        
+    def test_thread_isolation(self):
+        
+        debug("running tests")
+
+        env = os.environ.copy()
+        env['PATH_INFO'] = '/'
+        env['REQUEST_METHOD'] = 'GET'
+
+        t2 = thread_two(MockHandler(self.request_two), env, start_response)
+        t3 = thread_three(MockHandler(self.request_three), env, start_response)
+
+        try:
+            EV.set()
+            MockHandler(self.request_one)(env, start_response)
+        finally:
+            t2.join()
+            t3.join()
+            err = self.thread_errors()
+            if err:
+                import traceback 
+                for e in err:
+                    traceback.print_exception(*e)
+                    raise AssertionError("%s thread%s failed" %
+                                         (len(err), len(err) > 1 and 's' or
+                                          ''))
+                
=== tests/regressiontests/thread_isolation/models.py
==================================================================
--- tests/regressiontests/thread_isolation/models.py	(/mirror/django/trunk)	(revision 5420)

+++ tests/regressiontests/thread_isolation/models.py	(/local/django/mymultidb)	(revision 5420)

@@ -0,0 +1,19 @@

+from django.db import models
+
+# models
+class MQ(models.Model):
+    val = models.CharField(max_length=10)
+    class Meta:
+        app_label = 'ti'
+
+
+class MX(models.Model):
+    val = models.CharField(max_length=10)
+    class Meta:
+        app_label = 'ti'
+
+        
+class MY(models.Model):
+    val = models.CharField(max_length=10)
+    class Meta:
+        app_label = 'ti'

Property changes on: tests/regressiontests/thread_isolation
___________________________________________________________________
Name: svn:ignore
 +*.pyc
 +

=== tests/regressiontests/request_isolation	(new directory)
==================================================================
=== tests/regressiontests/request_isolation/__init__.py
==================================================================
=== tests/regressiontests/request_isolation/tests.py
==================================================================
--- tests/regressiontests/request_isolation/tests.py	(/mirror/django/trunk)	(revision 5420)

+++ tests/regressiontests/request_isolation/tests.py	(/local/django/mymultidb)	(revision 5420)

@@ -0,0 +1,100 @@

+# tests that db settings can change between requests
+import copy
+import os
+import unittest
+from django.conf import settings, UserSettingsHolder
+from django.core.handlers.wsgi import WSGIHandler
+from django.db import models, model_connection_name, _default, connection
+from django.http import HttpResponse
+from regressiontests.request_isolation.models import *
+
+
+# helpers
+class MockHandler(WSGIHandler):
+
+    def __init__(self, test):
+        self.test = test
+        super(MockHandler, self).__init__()
+        
+    def get_response(self, request):
+        # debug("mock handler answering %s, %s", path, request)
+        return HttpResponse(self.test(request))
+
+
+def debug(*arg):
+    pass
+    # msg, arg = arg[0], arg[1:]
+    # print msg % arg
+
+
+def start_response(code, headers):
+    debug("start response: %s %s", code, headers)
+    pass
+
+# tests
+class TestRequestIsolation(unittest.TestCase):
+
+    def setUp(self):
+        debug("setup")
+        self.settings = settings._target
+        settings._target = UserSettingsHolder(copy.deepcopy(settings._target))
+        settings.OTHER_DATABASES['_a']['MODELS'] = ['ri.MX']
+        settings.OTHER_DATABASES['_b']['MODELS'] = ['ri.MY']
+
+    def tearDown(self):
+        debug("teardown")
+        settings._target = self.settings
+
+    def testRequestIsolation(self):
+        env = os.environ.copy()
+        env['PATH_INFO'] = '/'
+        env['REQUEST_METHOD'] = 'GET'
+
+        def request_one(request):
+            """Start out with settings as originally configured"""
+            self.assertEqual(model_connection_name(MX), '_a')
+            self.assertEqual(
+                MX._default_manager.db.connection.settings.DATABASE_NAME,
+                settings.OTHER_DATABASES['_a']['DATABASE_NAME'])
+            self.assertEqual(model_connection_name(MY), '_b')
+            self.assertEqual(
+                MY._default_manager.db.connection.settings.DATABASE_NAME,
+                settings.OTHER_DATABASES['_b']['DATABASE_NAME'])
+
+        def request_two(request):
+            """Between the first and second requests, settings change to assign
+            model MY to a different connection
+            """
+            self.assertEqual(model_connection_name(MX), '_a')
+            self.assertEqual(
+                MX._default_manager.db.connection.settings.DATABASE_NAME,
+                settings.OTHER_DATABASES['_a']['DATABASE_NAME'])
+            self.assertEqual(model_connection_name(MY), _default)
+            self.assertEqual(
+                MY._default_manager.db.connection.settings.DATABASE_NAME,
+                settings.DATABASE_NAME)
+
+        def request_three(request):
+            """Between the 2nd and 3rd requests, the settings at the names in
+            OTHER_DATABASES have changed.
+            """
+            self.assertEqual(model_connection_name(MX), '_b')
+            self.assertEqual(
+                MX._default_manager.db.connection.settings.DATABASE_NAME,
+                settings.OTHER_DATABASES['_b']['DATABASE_NAME'])
+            self.assertEqual(model_connection_name(MY), '_a')
+            self.assertEqual(
+                MY._default_manager.db.connection.settings.DATABASE_NAME,
+                settings.OTHER_DATABASES['_a']['DATABASE_NAME'])
+    
+        MockHandler(request_one)(env, start_response)
+
+        settings.OTHER_DATABASES['_b']['MODELS'] = []
+        MockHandler(request_two)(env, start_response)
+
+        settings.OTHER_DATABASES['_b']['MODELS'] = ['ri.MY']
+        settings.OTHER_DATABASES['_b'], \
+            settings.OTHER_DATABASES['_a'] = \
+            settings.OTHER_DATABASES['_a'], \
+            settings.OTHER_DATABASES['_b']
+        MockHandler(request_three)(env, start_response)
=== tests/regressiontests/request_isolation/models.py
==================================================================
--- tests/regressiontests/request_isolation/models.py	(/mirror/django/trunk)	(revision 5420)

+++ tests/regressiontests/request_isolation/models.py	(/local/django/mymultidb)	(revision 5420)

@@ -0,0 +1,13 @@

+from django.db import models
+
+# models
+class MX(models.Model):
+    val = models.CharField(max_length=10)
+    class Meta:
+        app_label = 'ri'
+
+        
+class MY(models.Model):
+    val = models.CharField(max_length=10)
+    class Meta:
+        app_label = 'ri'

Property changes on: tests/regressiontests/request_isolation
___________________________________________________________________
Name: svn:ignore
 +*.pyc
 +

=== tests/runtests.py
==================================================================
--- tests/runtests.py	(/mirror/django/trunk)	(revision 5420)

+++ tests/runtests.py	(/local/django/mymultidb)	(revision 5420)

@@ -12,6 +12,14 @@

 
 
 CONTRIB_DIR_NAME = 'django.contrib'
+TEST_OTHER_DATABASES = {
+    '_a': { 'DATABASE_NAME': 'django_test_a.db',
+            'MODELS': [ 'multiple_databases.Artist',
+            'multiple_databases.Opus' ]},
+    '_b': { 'DATABASE_NAME': 'django_test_b.db',
+            'MODELS': [ 'multiple_databases.Widget',
+            'multiple_databases.DooHickey' ]}
+}
 MODEL_TESTS_DIR_NAME = 'modeltests'
 REGRESSION_TESTS_DIR_NAME = 'regressiontests'
 
@@ -99,6 +107,7 @@

 
     # Redirect some settings for the duration of these tests.
     settings.INSTALLED_APPS = ALWAYS_INSTALLED_APPS
+    settings.TEST_OTHER_DATABASES = TEST_OTHER_DATABASES
     settings.ROOT_URLCONF = 'urls'
     settings.TEMPLATE_DIRS = (os.path.join(os.path.dirname(__file__), TEST_TEMPLATE_DIR),)
     settings.USE_I18N = True
=== docs/settings.txt
==================================================================
--- docs/settings.txt	(/mirror/django/trunk)	(revision 5420)

+++ docs/settings.txt	(/local/django/mymultidb)	(revision 5420)

@@ -733,6 +733,13 @@

 See `allowed date format strings`_. See also ``DATE_FORMAT``,
 ``DATETIME_FORMAT``, ``TIME_FORMAT`` and ``YEAR_MONTH_FORMAT``.
 
+OTHER_DATABASES
+---------------
+
+Default: ``{}``
+
+Other database connections to use in addition to the default connection. See the `multiple database support docs`_.
+
 PREPEND_WWW
 -----------
 
=== docs/multiple_database_support.txt
==================================================================
--- docs/multiple_database_support.txt	(/mirror/django/trunk)	(revision 5420)

+++ docs/multiple_database_support.txt	(/local/django/mymultidb)	(revision 5420)

@@ -0,0 +1,163 @@

+========================
+Using Multiple Databases
+========================
+
+Standard Django practice is to use a single database connection for
+all models in all applications. However, Django supports configuring
+and using multiple database connections on a per-application, per-model
+or an ad-hoc basis. Using multiple database connections is optional.
+
+Configuring other database connections
+======================================
+
+Django's default database connection is configured via the settings
+DATABASE_ENGINE, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD,
+DATABASE_HOST, and DATABASE_PORT. Other connections are configured via
+the OTHER_DATABASES setting. Define OTHER_DATABASES as a dict, with a
+name for each connection as the key and a dict of settings as the
+value. In each OTHER_DATABASES entry (called a "named connection"),
+the keys are the same as the DATABASE_ENGINE, etc, settings used to
+configure the default connection. All keys are optional; any that are
+missing in a named connection's settings will inherit their values
+from the default connection.
+
+Here's an example::
+
+    DATABASE_ENGINE = 'postgresql'
+    DATABASE_NAME = 'django_apps'
+    DATABASE_USER = 'default_user'
+    DATABASE_PASSWORD = 'xxx'
+	
+    OTHER_DATABASES = {
+        'local': { 'DATABASE_ENGINE': 'sqlite3',
+                   'DATABASE_NAME': '/tmp/cache.db' },
+        'public': { 'DATABASE_HOST': 'public',
+                    'DATABASE_USER': 'public_user',
+                    'DATABASE_PASSWORD': 'xxx' }
+        'private': { 'DATABASE_HOST': 'private',
+                     'DATABASE_USER': 'private_user',
+                     'DATABASE_PASSWORD': 'xxx' }
+    }
+
+In addition to the DATABASE_* settings, each named connection in
+OTHER_DATABASES may optionally include a MODELS setting. This should
+be a list of app or app.model names, and is used to configure which
+models should use this connection instead of the default connection.
+
+Here's the example above, with ``MODELS``::
+
+    OTHER_DATABASES = {
+        'local': { 'DATABASE_ENGINE': 'sqlite3',
+                   'DATABASE_NAME': '/tmp/cache.db',
+                   # A model name: only the model ContentItem
+                   # with the app_label myapp will use this connection
+                   'MODELS': ['myapp.ContentItem'] },
+        'public': { 'DATABASE_HOST': 'public',
+                    'DATABASE_USER': 'public_user',
+                    'DATABASE_PASSWORD': 'xxx',
+	            # Two models in myapp will use the connection
+                    # named 'public', as will ALL models in 
+                    # django.contribe.comments
+		    'MODELS': ['myapp.Blog','myapp.Article',
+                               'django.contrib.comments' ] }
+        # No models or apps are configured to use the private db
+        'private': { 'DATABASE_HOST': 'private',
+                     'DATABASE_USER': 'private_user',
+                     'DATABASE_PASSWORD': 'xxx' }
+    }
+
+Accessing a model's connection
+==============================
+
+Each manager has a ``db`` attribute that can be used to access the model's
+connection. Access the ``db`` attribute of a model's manager to obtain the
+model's currently configured connection. 
+
+Example::
+
+    from django.db import models
+
+    class Blog(models.Model)
+        name = models.CharField(maxlength=50)
+
+    class Article(models.Model)
+	blog = models.ForeignKey(Blog)
+        title = models.CharField(maxlength=100)
+        slug = models.SlugField()
+        summary = models.CharField(maxlength=500)
+        body = models.TextField()
+
+    class ContentItem(models.Model)
+        slug = models.SlugField()
+        mimetype = models.CharField(maxlength=50)
+	file = models.FileField()
+	
+    # Get a ConnectionInfo instance that describes the connection
+    article_db = Article.objects.db
+    
+    # Get a connection and a cursor
+    connection = article_db.connection
+    cursor = connection.cursor()
+
+    # Get the ``quote_name`` function from the backend
+    qn = article_db.backend.quote_name
+
+Ordinarily you won't have to access a model's connection directly;
+just use the model and manager normally and they will use the
+connection configured for the model.
+
+ConnectionInfo objects
+======================
+
+FIXME Describe the ConnectionInfo object and each of its attributes.
+
+
+Accessing connections by name
+=============================
+
+Access named connections directly through
+``django.db.connections``. Each entry in ``django.db.connections`` is
+a ``ConnectionInfo`` instance bound to the settings configured in the
+OTHER_DATABASES entry under the same key. 
+
+Example::
+
+    from django.db import connections
+
+    private_db = connections['private']
+    cursor = private_db.connection.cursor()
+
+
+Using transactions with other database connections
+==================================================
+
+Transaction managed state applies across all connections
+commit/rollback apply to all connections by default
+but you can specify individual connections or lists or dicts of connections
+
+
+Changing model connections on the fly
+=====================================
+
+Here's an example of primitive mirroring::
+ 
+    # Read all articles from the private db
+    # Note that we pull the articles into a list; this is necessary
+    # because query sets are lazy. If we were to change the model's
+    # connection without copying the articles into a local list, we'd
+    # wind up reading from public instead of private.
+
+    Article.objects.db = connections['private']
+    all_articles = list(Article.objects.all())
+    
+    # Save each article in the public db
+    Article.objects.db = connections['public']
+    for article in all_articles:
+        article.save()
+
+Thread and request isolation
+============================
+
+connections close after each request
+connection settings are thread-local
+
