Code

Ticket #4747: mymultidb_trunk7534_20080520.diff

File mymultidb_trunk7534_20080520.diff, 143.1 KB (added by Koen Biermans <koen.biermans@…>, 6 years ago)

effort to get multidb into trunk r7534

Line 
1=== django/test/simple.py
2==================================================================
3--- django/test/simple.py       (/mirror/django/trunk)  (revision 5420)
4
5+++ django/test/simple.py       (/local/django/mymultidb)       (revision 5420)
6
7@@ -138,10 +138,11 @@
8
9     for test in extra_tests:
10         suite.addTest(test)
11 
12-    old_name = settings.DATABASE_NAME
13+    old_database_name = settings.DATABASE_NAME
14+    old_other_databases = settings.OTHER_DATABASES
15     create_test_db(verbosity, autoclobber=not interactive)
16     result = unittest.TextTestRunner(verbosity=verbosity).run(suite)
17-    destroy_test_db(old_name, verbosity)
18+    destroy_test_db(old_database_name, old_other_databases, verbosity)
19     
20     teardown_test_environment()
21     
22=== django/test/utils.py
23==================================================================
24--- django/test/utils.py        (/mirror/django/trunk)  (revision 5420)
25
26+++ django/test/utils.py        (/local/django/mymultidb)       (revision 5420)
27
28@@ -1,6 +1,6 @@
29
30 import sys, time, os
31-from django.conf import settings
32-from django.db import connection, get_creation_module
33+from django.conf import settings, UserSettingsHolder
34+from django.db import connections, get_creation_module, _default
35 from django.core import mail
36 from django.core.management import call_command
37 from django.dispatch import dispatcher
38@@ -78,27 +78,74 @@
39
40     elif hasattr(connection.connection, "set_isolation_level"):
41         connection.connection.set_isolation_level(0)
42 
43-def get_mysql_create_suffix():
44+def get_mysql_create_suffix(settings):
45     suffix = []
46-    if settings.TEST_DATABASE_CHARSET:
47-        suffix.append('CHARACTER SET %s' % settings.TEST_DATABASE_CHARSET)
48-    if settings.TEST_DATABASE_COLLATION:
49-        suffix.append('COLLATE %s' % settings.TEST_DATABASE_COLLATION)
50+    if settings.DATABASE_CHARSET:
51+        suffix.append('CHARACTER SET %s' % settings.DATABASE_CHARSET)
52+    if settings.DATABASE_COLLATION:
53+        suffix.append('COLLATE %s' % settings.DATABASE_COLLATION)
54     return ' '.join(suffix)
55 
56 def get_postgresql_create_suffix():
57-    assert settings.TEST_DATABASE_COLLATION is None, "PostgreSQL does not support collation setting at database creation time."
58-    if settings.TEST_DATABASE_CHARSET:
59-        return "WITH ENCODING '%s'" % settings.TEST_DATABASE_CHARSET
60+    assert settings.DATABASE_COLLATION is None, "PostgreSQL does not support collation setting at database creation time."
61+    if settings.DATABASE_CHARSET:
62+        return "WITH ENCODING '%s'" % settings.DATABASE_CHARSET
63     return ''
64 
65 def create_test_db(verbosity=1, autoclobber=False):
66+
67+    if settings.DATABASE_ENGINE == "sqlite3":
68+        if settings.TEST_DATABASE_NAME and settings.TEST_DATABASE_NAME != ":memory:":
69+            TEST_DATABASE_NAME = settings.TEST_DATABASE_NAME
70+        else:
71+            TEST_DATABASE_NAME = ":memory:"
72+    else:
73+        if settings.TEST_DATABASE_NAME:
74+            TEST_DATABASE_NAME = settings.TEST_DATABASE_NAME
75+        else:
76+            TEST_DATABASE_NAME = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
77+
78+    settings.DATABASE_NAME = TEST_DATABASE_NAME
79+    settings.DATABASE_CHARSET = settings.TEST_DATABASE_CHARSET
80+    settings.DATABASE_COLLATION = settings.TEST_DATABASE_COLLATION
81+
82+    # default database
83+    _create_test_db(settings, verbosity=verbosity, autoclobber=autoclobber)
84+
85+    # other databases
86+    settings.OTHER_DATABASES = settings.TEST_OTHER_DATABASES
87+    for db in settings.OTHER_DATABASES:
88+        info = settings.OTHER_DATABASES[db]
89+        database = UserSettingsHolder(settings)
90+        for k, v in info.items():
91+            setattr(database, k, v)
92+        _create_test_db(database, verbosity=verbosity, autoclobber=autoclobber, connection_name=db)
93+
94+##    for cnx in connections:
95+##        connections[cnx].connection.close()
96+
97+    call_command('syncdb', verbosity=verbosity, interactive=False)
98+
99+    if settings.CACHE_BACKEND.startswith('db://'):
100+        cache_name = settings.CACHE_BACKEND[len('db://'):]
101+        call_command('createcachetable', cache_name)
102+
103+    # Get a cursor (even though we don't need one yet). This has
104+    # the side effect of initializing the test database.
105+##    for cnx in connections:
106+##        cursor = connections[cnx].connection.cursor()
107+##    cursor = connection.cursor()
108+
109+    return TEST_DATABASE_NAME
110+
111+def _create_test_db(settings, verbosity=1, autoclobber=False, connection_name=_default):
112     """
113     Creates a test database, prompting the user for confirmation if the
114     database already exists. Returns the name of the test database created.
115     """
116+    connection = connections[connection_name].connection
117     # If the database backend wants to create the test DB itself, let it
118-    creation_module = get_creation_module()
119+    creation_module = connections[connection_name].get_creation_module()
120     if hasattr(creation_module, "create_test_db"):
121         creation_module.create_test_db(settings, connection, verbosity, autoclobber)
122         return
123@@ -109,19 +156,18 @@
124
125     # in-memory database. Using the TEST_DATABASE_NAME setting you can still choose
126     # to run on a physical database.
127     if settings.DATABASE_ENGINE == "sqlite3":
128-        if settings.TEST_DATABASE_NAME and settings.TEST_DATABASE_NAME != ":memory:":
129-            TEST_DATABASE_NAME = settings.TEST_DATABASE_NAME
130+        if settings.DATABASE_NAME != ":memory:":
131             # Erase the old test database
132             if verbosity >= 1:
133                 print "Destroying old test database..."
134-            if os.access(TEST_DATABASE_NAME, os.F_OK):
135+            if os.access(settings.DATABASE_NAME, os.F_OK):
136                 if not autoclobber:
137-                    confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % TEST_DATABASE_NAME)
138+                    confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % settings.DATABASE_NAME)
139                 if autoclobber or confirm == 'yes':
140                   try:
141                       if verbosity >= 1:
142                           print "Destroying old test database..."
143-                      os.remove(TEST_DATABASE_NAME)
144+                      os.remove(settings.DATABASE_NAME)
145                   except Exception, e:
146                       sys.stderr.write("Got an error deleting the old test database: %s\n" % e)
147                       sys.exit(2)
148@@ -130,20 +176,15 @@
149
150                     sys.exit(1)
151             if verbosity >= 1:
152                 print "Creating test database..."
153-        else:
154-            TEST_DATABASE_NAME = ":memory:"
155     else:
156         suffix = {
157             'postgresql': get_postgresql_create_suffix,
158             'postgresql_psycopg2': get_postgresql_create_suffix,
159             'mysql': get_mysql_create_suffix,
160             'mysql_old': get_mysql_create_suffix,
161-        }.get(settings.DATABASE_ENGINE, lambda: '')()
162-        if settings.TEST_DATABASE_NAME:
163-            TEST_DATABASE_NAME = settings.TEST_DATABASE_NAME
164-        else:
165-            TEST_DATABASE_NAME = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
166+        }.get(settings.DATABASE_ENGINE, lambda: '')(settings)
167 
168+        connection = connections[connection_name].connection
169         qn = connection.ops.quote_name
170 
171         # Create the test database and connect to it. We need to autocommit
172@@ -172,37 +213,40 @@
173
174                 print "Tests cancelled."
175                 sys.exit(1)
176 
177-    connection.close()
178-    settings.DATABASE_NAME = TEST_DATABASE_NAME
179 
180-    call_command('syncdb', verbosity=verbosity, interactive=False)
181+def destroy_test_db(old_database_name, old_databases, verbosity=1):
182+    if verbosity >= 1:
183+        print "Destroying test database(s)..."
184+##    connection.close()
185+    for cnx in connections.keys():
186+        connections[cnx].close()
187+##        connections.reset()
188+    TEST_DATABASE_NAME = settings.DATABASE_NAME
189+    settings.DATABASE_NAME = old_database_name
190+    _destroy_test_db(settings, TEST_DATABASE_NAME, verbosity=verbosity)
191+    TEST_OTHER_DATABASES = settings.OTHER_DATABASES
192+    settings.OTHER_DATABASES = old_databases
193+    for db in TEST_OTHER_DATABASES:
194+        info = TEST_OTHER_DATABASES[db]
195+        database = UserSettingsHolder(settings)
196+        for k, v in info.items():
197+            setattr(database, k, v)
198+        if db in old_databases:
199+            _destroy_test_db(database, info['DATABASE_NAME'], verbosity=verbosity, connection_name=db)
200 
201-    if settings.CACHE_BACKEND.startswith('db://'):
202-        cache_name = settings.CACHE_BACKEND[len('db://'):]
203-        call_command('createcachetable', cache_name)
204-
205-    # Get a cursor (even though we don't need one yet). This has
206-    # the side effect of initializing the test database.
207-    cursor = connection.cursor()
208-
209-    return TEST_DATABASE_NAME
210-
211-def destroy_test_db(old_database_name, verbosity=1):
212+def _destroy_test_db(settings, TEST_DATABASE_NAME, verbosity=1, connection_name=_default):
213+    connection = connections[connection_name].connection
214     # If the database wants to drop the test DB itself, let it
215-    creation_module = get_creation_module()
216+    creation_module = connections[connection_name].get_creation_module()
217     if hasattr(creation_module, "destroy_test_db"):
218         creation_module.destroy_test_db(settings, connection, old_database_name, verbosity)
219         return
220 
221-    if verbosity >= 1:
222-        print "Destroying test database..."
223-    connection.close()
224-    TEST_DATABASE_NAME = settings.DATABASE_NAME
225-    settings.DATABASE_NAME = old_database_name
226     if settings.DATABASE_ENGINE == "sqlite3":
227         if TEST_DATABASE_NAME and TEST_DATABASE_NAME != ":memory:":
228             # Remove the SQLite database file
229-            os.remove(TEST_DATABASE_NAME)
230+##            os.remove(TEST_DATABASE_NAME)
231+            pass
232     else:
233         # Remove the test database to clean up after
234         # ourselves. Connect to the previous database (not the test database)
235=== django/db/models/sql/where.py
236==================================================================
237--- django/db/models/sql/where.py       (/mirror/django/trunk)  (revision 5420)
238
239+++ django/db/models/sql/where.py       (/local/django/mymultidb)       (revision 5420)
240
241@@ -4,11 +4,12 @@
242
243 import datetime
244 
245 from django.utils import tree
246-from django.db import connection
247 from django.db.models.fields import Field
248 from django.db.models.query_utils import QueryWrapper
249 from datastructures import EmptyResultSet, FullResultSet
250 
251+from copy import deepcopy
252+
253 # Connection types
254 AND = 'AND'
255 OR = 'OR'
256@@ -26,6 +27,20 @@
257
258     """
259     default = AND
260 
261+    def __init__(self, connection, *args, **kwargs):
262+        self.connection = connection
263+        super(WhereNode, self).__init__(*args, **kwargs)
264+
265+    def __deepcopy__(self, memo):
266+        x = WhereNode.__new__(WhereNode)
267+        memo[id(self)] = x
268+        for n, v in self.__dict__.iteritems():
269+            if n != 'connection':
270+                setattr(x, n, deepcopy(v, memo))
271+            else:
272+                setattr(x, n, self.connection)
273+        return x
274+
275     def as_sql(self, node=None, qn=None):
276         """
277         Returns the SQL version of the where clause and the value to be
278@@ -38,7 +53,7 @@
279
280         if node is None:
281             node = self
282         if not qn:
283-            qn = connection.ops.quote_name
284+            qn = self.connection.ops.quote_name
285         if not node.children:
286             return None, []
287         result = []
288@@ -99,11 +114,12 @@
289
290             lhs = '%s.%s' % (qn(table_alias), qn(name))
291         else:
292             lhs = qn(name)
293-        db_type = field and field.db_type() or None
294-        field_sql = connection.ops.field_cast_sql(db_type) % lhs
295 
296+        db_type = field and field.db_type(self.connection.connection_name) or None
297+        field_sql = self.connection.ops.field_cast_sql(db_type) % lhs
298+       
299         if isinstance(value, datetime.datetime):
300-            cast_sql = connection.ops.datetime_cast_sql()
301+            cast_sql = self.connection.ops.datetime_cast_sql()
302         else:
303             cast_sql = '%s'
304 
305@@ -116,11 +132,11 @@
306
307         else:
308             extra = ''
309 
310-        if lookup_type in connection.operators:
311-            format = "%s %%s %s" % (connection.ops.lookup_cast(lookup_type),
312+        if lookup_type in self.connection.operators:
313+            format = "%s %%s %s" % (self.connection.ops.lookup_cast(lookup_type),
314                     extra)
315             return (format % (field_sql,
316-                    connection.operators[lookup_type] % cast_sql), params)
317+                    self.connection.operators[lookup_type] % cast_sql), params)
318 
319         if lookup_type == 'in':
320             if not value:
321@@ -132,15 +148,15 @@
322
323         elif lookup_type in ('range', 'year'):
324             return ('%s BETWEEN %%s and %%s' % field_sql, params)
325         elif lookup_type in ('month', 'day'):
326-            return ('%s = %%s' % connection.ops.date_extract_sql(lookup_type,
327+            return ('%s = %%s' % self.connection.ops.date_extract_sql(lookup_type,
328                     field_sql), params)
329         elif lookup_type == 'isnull':
330             return ('%s IS %sNULL' % (field_sql, (not value and 'NOT ' or '')),
331                     params)
332         elif lookup_type == 'search':
333-            return (connection.ops.fulltext_search_sql(field_sql), params)
334+            return (self.connection.ops.fulltext_search_sql(field_sql), params)
335         elif lookup_type in ('regex', 'iregex'):
336-            return connection.ops.regex_lookup(lookup_type) % (field_sql, cast_sql), params
337+            return self.connection.ops.regex_lookup(lookup_type) % (field_sql, cast_sql), params
338 
339         raise TypeError('Invalid lookup_type: %r' % lookup_type)
340 
341=== django/db/models/sql/query.py
342==================================================================
343--- django/db/models/sql/query.py       (/mirror/django/trunk)  (revision 5420)
344
345+++ django/db/models/sql/query.py       (/local/django/mymultidb)       (revision 5420)
346
347@@ -60,7 +60,7 @@
348
349         # SQL-related attributes
350         self.select = []
351         self.tables = []    # Aliases in the order they are created.
352-        self.where = where()
353+        self.where = where(self.connection)
354         self.where_class = where
355         self.group_by = []
356         self.having = []
357@@ -215,7 +215,7 @@
358
359         obj.related_select_cols = []
360         obj.related_select_fields = []
361         if obj.distinct and len(obj.select) > 1:
362-            obj = self.clone(CountQuery, _query=obj, where=self.where_class(),
363+            obj = self.clone(CountQuery, _query=obj, where=self.where_class(self.connection),
364                     distinct=False)
365             obj.select = []
366             obj.extra_select = {}
367@@ -346,10 +346,10 @@
368
369                 self.where.add(EverythingNode(), AND)
370         elif self.where:
371             # rhs has an empty where clause.
372-            w = self.where_class()
373+            w = self.where_class(self.connection)
374             w.add(EverythingNode(), AND)
375         else:
376-            w = self.where_class()
377+            w = self.where_class(self.connection)
378         self.where.add(w, connector)
379 
380         # Selection columns and extra extensions are those provided by 'rhs'.
381=== django/db/models/sql/subqueries.py
382==================================================================
383--- django/db/models/sql/subqueries.py  (/mirror/django/trunk)  (revision 5420)
384
385+++ django/db/models/sql/subqueries.py  (/local/django/mymultidb)       (revision 5420)
386
387@@ -47,7 +47,7 @@
388
389         for related in cls._meta.get_all_related_many_to_many_objects():
390             if not isinstance(related.field, generic.GenericRelation):
391                 for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
392-                    where = self.where_class()
393+                    where = self.where_class(self.connection)
394                     where.add((None, related.field.m2m_reverse_name(),
395                             related.field, 'in',
396                             pk_list[offset : offset+GET_ITERATOR_CHUNK_SIZE]),
397@@ -55,14 +55,14 @@
398
399                     self.do_query(related.field.m2m_db_table(), where)
400 
401         for f in cls._meta.many_to_many:
402-            w1 = self.where_class()
403+            w1 = self.where_class(self.connection)
404             if isinstance(f, generic.GenericRelation):
405                 from django.contrib.contenttypes.models import ContentType
406                 field = f.rel.to._meta.get_field(f.content_type_field_name)
407                 w1.add((None, field.column, field, 'exact',
408                         ContentType.objects.get_for_model(cls).id), AND)
409             for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
410-                where = self.where_class()
411+                where = self.where_class(self.connection)
412                 where.add((None, f.m2m_column_name(), f, 'in',
413                         pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE]),
414                         AND)
415@@ -79,7 +79,7 @@
416
417         lot of values in pk_list.
418         """
419         for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
420-            where = self.where_class()
421+            where = self.where_class(self.connection)
422             field = self.model._meta.pk
423             where.add((None, field.column, field, 'in',
424                     pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE]), AND)
425@@ -178,7 +178,7 @@
426
427 
428         # Now we adjust the current query: reset the where clause and get rid
429         # of all the tables we don't need (since they're in the sub-select).
430-        self.where = self.where_class()
431+        self.where = self.where_class(self.connection)
432         if self.related_updates or must_pre_select:
433             # Either we're using the idents in multiple update queries (so
434             # don't want them to change), or the db backend doesn't support
435@@ -202,7 +202,7 @@
436
437         This is used by the QuerySet.delete_objects() method.
438         """
439         for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
440-            self.where = self.where_class()
441+            self.where = self.where_class(self.connection)
442             f = self.model._meta.pk
443             self.where.add((None, f.column, f, 'in',
444                     pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE]),
445=== django/db/models/base.py
446==================================================================
447--- django/db/models/base.py    (/mirror/django/trunk)  (revision 5420)
448
449+++ django/db/models/base.py    (/local/django/mymultidb)       (revision 5420)
450
451@@ -12,7 +12,7 @@
452
453 from django.db.models.fields.related import OneToOneRel, ManyToOneRel, OneToOneField
454 from django.db.models.query import delete_objects, Q
455 from django.db.models.options import Options, AdminOptions
456-from django.db import connection, transaction
457+from django.db import transaction
458 from django.db.models import signals
459 from django.db.models.loading import register_models, get_model
460 from django.dispatch import dispatcher
461@@ -329,7 +329,7 @@
462
463                 result = manager._insert(values, return_id=update_pk)
464             else:
465                 # Create a new record with defaults for everything.
466-                result = manager._insert([(meta.pk, connection.ops.pk_default_value())], return_id=update_pk, raw_values=True)
467+                result = manager._insert([(meta.pk, manager.db.connection.ops.pk_default_value())], return_id=update_pk, raw_values=True)
468 
469             if update_pk:
470                 setattr(self, meta.pk.attname, result)
471@@ -419,7 +419,7 @@
472
473     def _get_next_or_previous_in_order(self, is_next):
474         cachename = "__%s_order_cache" % is_next
475         if not hasattr(self, cachename):
476-            qn = connection.ops.quote_name
477+            qn = self._default_manager.db.connection.ops.quote_name
478             op = is_next and '>' or '<'
479             order = not is_next and '-_order' or '_order'
480             order_field = self._meta.order_with_respect_to
481=== django/db/models/manager.py
482==================================================================
483--- django/db/models/manager.py (/mirror/django/trunk)  (revision 5420)
484
485+++ django/db/models/manager.py (/local/django/mymultidb)       (revision 5420)
486
487@@ -1,8 +1,9 @@
488
489 import copy
490 
491 from django.db.models.query import QuerySet, EmptyQuerySet, insert_query
492+from django.db import ConnectionInfoDescriptor
493 from django.dispatch import dispatcher
494-from django.db.models import signals
495+from django.db.models import signals, get_apps, get_models
496 from django.db.models.fields import FieldDoesNotExist
497 
498 def ensure_default_manager(sender):
499@@ -22,6 +23,8 @@
500
501     # Tracks each time a Manager instance is created. Used to retain order.
502     creation_counter = 0
503 
504+    db = ConnectionInfoDescriptor()
505+
506     def __init__(self):
507         super(Manager, self).__init__()
508         # Increase the creation counter, and save our local copy.
509=== django/db/models/options.py
510==================================================================
511--- django/db/models/options.py (/mirror/django/trunk)  (revision 5420)
512
513+++ django/db/models/options.py (/local/django/mymultidb)       (revision 5420)
514
515@@ -53,6 +53,8 @@
516
517         self.module_name = self.object_name.lower()
518         self.verbose_name = get_verbose_name(self.object_name)
519 
520+        self.model = cls
521+
522         # Next, apply any overridden values from 'class Meta'.
523         if self.meta:
524             meta_attrs = self.meta.__dict__.copy()
525@@ -84,8 +86,8 @@
526
527         del self.meta
528 
529     def _prepare(self, model):
530-        from django.db import connection
531         from django.db.backends.util import truncate_name
532+        from django.db import connections, connection_name_app_model
533         if self.order_with_respect_to:
534             self.order_with_respect_to = self.get_field(self.order_with_respect_to)
535             self.ordering = ('_order',)
536@@ -107,7 +109,8 @@
537
538         # If the db_table wasn't provided, use the app_label + module_name.
539         if not self.db_table:
540             self.db_table = "%s_%s" % (self.app_label, self.module_name)
541-            self.db_table = truncate_name(self.db_table, connection.ops.max_name_length())
542+            connection_name = connection_name_app_model(self.app_label, self.object_name)
543+            self.db_table = truncate_name(self.db_table, connections[connection_name].connection.ops.max_name_length())
544 
545     def add_field(self, field):
546         # Insert the given field in the order in which it was created, using
547=== django/db/models/__init__.py
548==================================================================
549--- django/db/models/__init__.py        (/mirror/django/trunk)  (revision 5420)
550
551+++ django/db/models/__init__.py        (/local/django/mymultidb)       (revision 5420)
552
553@@ -1,7 +1,6 @@
554
555 from django.conf import settings
556 from django.core.exceptions import ObjectDoesNotExist, ImproperlyConfigured
557 from django.core import validators
558-from django.db import connection
559 from django.db.models.loading import get_apps, get_app, get_models, get_model, register_models
560 from django.db.models.query import Q
561 from django.db.models.manager import Manager
562=== django/db/models/fields/__init__.py
563==================================================================
564--- django/db/models/fields/__init__.py (/mirror/django/trunk)  (revision 5420)
565
566+++ django/db/models/fields/__init__.py (/local/django/mymultidb)       (revision 5420)
567
568@@ -7,7 +7,7 @@
569
570 except ImportError:
571     from django.utils import _decimal as decimal    # for Python 2.3
572 
573-from django.db import get_creation_module
574+from django.db import connections
575 from django.db.models import signals
576 from django.db.models.query_utils import QueryWrapper
577 from django.dispatch import dispatcher
578@@ -144,7 +144,7 @@
579
580         """
581         return value
582 
583-    def db_type(self):
584+    def db_type(self, connection):
585         """
586         Returns the database column data type for this field, taking into
587         account the DATABASE_ENGINE setting.
588@@ -165,7 +165,7 @@
589
590         # can implement db_type() instead of get_internal_type() to specify
591         # exactly which wacky database column type you want to use.
592         try:
593-            return get_creation_module().DATA_TYPES[self.get_internal_type()] % self.__dict__
594+            return connections[connection].get_creation_module().DATA_TYPES[self.get_internal_type()] % self.__dict__
595         except KeyError:
596             return None
597 
598=== django/db/models/fields/related.py
599==================================================================
600--- django/db/models/fields/related.py  (/mirror/django/trunk)  (revision 5420)
601
602+++ django/db/models/fields/related.py  (/local/django/mymultidb)       (revision 5420)
603
604@@ -1,4 +1,4 @@
605
606-from django.db import connection, transaction
607+from django.db import transaction
608 from django.db.models import signals, get_model
609 from django.db.models.fields import AutoField, Field, IntegerField, PositiveIntegerField, PositiveSmallIntegerField, get_ul_class
610 from django.db.models.related import RelatedObject
611@@ -368,6 +368,8 @@
612
613             # target_col_name: the PK colname in join_table for the target object
614             # *objs - objects to add. Either object instances, or primary keys of object instances.
615 
616+            connection = self.model._default_manager.db.connection
617+
618             # If there aren't any objects, there is nothing to do.
619             if objs:
620                 # Check that all the objects are of the right type
621@@ -391,13 +393,15 @@
622
623                     cursor.execute("INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \
624                         (self.join_table, source_col_name, target_col_name),
625                         [self._pk_val, obj_id])
626-                transaction.commit_unless_managed()
627+                transaction.commit_unless_managed(connection)
628 
629         def _remove_items(self, source_col_name, target_col_name, *objs):
630             # source_col_name: the PK colname in join_table for the source object
631             # target_col_name: the PK colname in join_table for the target object
632             # *objs - objects to remove
633 
634+            connection = self.model._default_manager.db.connection
635+
636             # If there aren't any objects, there is nothing to do.
637             if objs:
638                 # Check that all the objects are of the right type
639@@ -413,15 +417,16 @@
640
641                     (self.join_table, source_col_name,
642                     target_col_name, ",".join(['%s'] * len(old_ids))),
643                     [self._pk_val] + list(old_ids))
644-                transaction.commit_unless_managed()
645+                transaction.commit_unless_managed(connection)
646 
647         def _clear_items(self, source_col_name):
648             # source_col_name: the PK colname in join_table for the source object
649+            connection = self.model._default_manager.db.connection
650             cursor = connection.cursor()
651             cursor.execute("DELETE FROM %s WHERE %s = %%s" % \
652                 (self.join_table, source_col_name),
653                 [self._pk_val])
654-            transaction.commit_unless_managed()
655+            transaction.commit_unless_managed(connection)
656 
657     return ManyRelatedManager
658 
659@@ -445,7 +450,7 @@
660
661         superclass = rel_model._default_manager.__class__
662         RelatedManager = create_many_related_manager(superclass)
663 
664-        qn = connection.ops.quote_name
665+        qn = rel_model._default_manager.db.connection.ops.quote_name
666         manager = RelatedManager(
667             model=rel_model,
668             core_filters={'%s__pk' % self.related.field.name: instance._get_pk_val()},
669@@ -486,7 +491,7 @@
670
671         superclass = rel_model._default_manager.__class__
672         RelatedManager = create_many_related_manager(superclass)
673 
674-        qn = connection.ops.quote_name
675+        qn = rel_model._default_manager.db.connection.ops.quote_name
676         manager = RelatedManager(
677             model=rel_model,
678             core_filters={'%s__pk' % self.field.related_query_name(): instance._get_pk_val()},
679@@ -673,15 +678,15 @@
680
681         defaults.update(kwargs)
682         return super(ForeignKey, self).formfield(**defaults)
683 
684-    def db_type(self):
685+    def db_type(self, connection):
686         # The database column type of a ForeignKey is the column type
687         # of the field to which it points. An exception is if the ForeignKey
688         # points to an AutoField/PositiveIntegerField/PositiveSmallIntegerField,
689         # in which case the column type is simply that of an IntegerField.
690         rel_field = self.rel.get_related_field()
691         if isinstance(rel_field, (AutoField, PositiveIntegerField, PositiveSmallIntegerField)):
692-            return IntegerField().db_type()
693-        return rel_field.db_type()
694+            return IntegerField().db_type(connection)
695+        return rel_field.db_type(connection)
696 
697 class OneToOneField(ForeignKey):
698     """
699@@ -828,7 +833,7 @@
700
701             defaults['initial'] = [i._get_pk_val() for i in defaults['initial']]
702         return super(ManyToManyField, self).formfield(**defaults)
703 
704-    def db_type(self):
705+    def db_type(self, connection):
706         # A ManyToManyField is not represented by a single column,
707         # so return None.
708         return None
709=== django/db/models/query.py
710==================================================================
711--- django/db/models/query.py   (/mirror/django/trunk)  (revision 5420)
712
713+++ django/db/models/query.py   (/local/django/mymultidb)       (revision 5420)
714
715@@ -1,7 +1,7 @@
716
717 import warnings
718 
719 from django.conf import settings
720-from django.db import connection, transaction, IntegrityError
721+from django.db import transaction, IntegrityError
722 from django.db.models.fields import DateField, FieldDoesNotExist
723 from django.db.models.query_utils import Q
724 from django.db.models import signals, sql
725@@ -20,7 +20,7 @@
726
727     "Represents a lazy database lookup for a set of objects"
728     def __init__(self, model=None, query=None):
729         self.model = model
730-        self.query = query or sql.Query(self.model, connection)
731+        self.query = query or sql.Query(self.model, self.model._default_manager.db.connection)
732         self._result_cache = None
733         self._iter = None
734 
735@@ -689,10 +689,10 @@
736
737                     instance=instance)
738 
739         pk_list = [pk for pk,instance in seen_objs[cls]]
740-        del_query = sql.DeleteQuery(cls, connection)
741+        del_query = sql.DeleteQuery(cls, cls._default_manager.db.connection)
742         del_query.delete_batch_related(pk_list)
743 
744-        update_query = sql.UpdateQuery(cls, connection)
745+        update_query = sql.UpdateQuery(cls, cls._default_manager.db.connection)
746         for field in cls._meta.fields:
747             if field.rel and field.null and field.rel.to in seen_objs:
748                 update_query.clear_related(field, pk_list)
749@@ -701,7 +701,7 @@
750
751     for cls in ordered_classes:
752         seen_objs[cls].reverse()
753         pk_list = [pk for pk,instance in seen_objs[cls]]
754-        del_query = sql.DeleteQuery(cls, connection)
755+        del_query = sql.DeleteQuery(cls, cls._default_manager.db.connection)
756         del_query.delete_batch(pk_list)
757 
758         # Last cleanup; set NULLs where there once was a reference to the
759@@ -724,7 +724,7 @@
760
761     the InsertQuery class and is how Model.save() is implemented. It is not
762     part of the public API.
763     """
764-    query = sql.InsertQuery(model, connection)
765+    query = sql.InsertQuery(model, model._default_manager.db.connection)
766     query.insert_values(values, raw_values)
767     return query.execute_sql(return_id)
768 
769=== django/db/__init__.py
770==================================================================
771--- django/db/__init__.py       (/mirror/django/trunk)  (revision 5420)
772
773+++ django/db/__init__.py       (/local/django/mymultidb)       (revision 5420)
774
775@@ -1,74 +1,428 @@
776
777 import os
778-from django.conf import settings
779+from django.conf import settings, UserSettingsHolder
780 from django.core import signals
781 from django.core.exceptions import ImproperlyConfigured
782 from django.dispatch import dispatcher
783 from django.utils.functional import curry
784 
785+try:
786+    # Only exists in Python 2.4+
787+    from threading import local
788+except ImportError:
789+    # Import copy of _thread_local.py from Python 2.4
790+    from django.utils._threading_local import local
791+
792 __all__ = ('backend', 'connection', 'DatabaseError', 'IntegrityError')
793 
794+
795+# singleton to represent the default connection in connections
796+class dummy(object):
797+    def __repr__(self):
798+        return self.__str__()
799+    def __str__(self):
800+        return '<default>'
801+_default = dummy()
802+del dummy
803+
804+
805+# storage for local default connection
806+_local = local()
807+
808 if not settings.DATABASE_ENGINE:
809     settings.DATABASE_ENGINE = 'dummy'
810 
811-try:
812-    # Most of the time, the database backend will be one of the official
813-    # backends that ships with Django, so look there first.
814-    _import_path = 'django.db.backends.'
815-    backend = __import__('%s%s.base' % (_import_path, settings.DATABASE_ENGINE), {}, {}, [''])
816-    creation = __import__('%s%s.creation' % (_import_path, settings.DATABASE_ENGINE), {}, {}, [''])
817-except ImportError, e:
818-    # If the import failed, we might be looking for a database backend
819-    # distributed external to Django. So we'll try that next.
820-    try:
821-        _import_path = ''
822-        backend = __import__('%s.base' % settings.DATABASE_ENGINE, {}, {}, [''])
823-        creation = __import__('%s.creation' % settings.DATABASE_ENGINE, {}, {}, [''])
824-    except ImportError, e_user:
825-        # The database backend wasn't found. Display a helpful error message
826-        # listing all possible (built-in) database backends.
827-        backend_dir = os.path.join(__path__[0], 'backends')
828-        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')]
829-        available_backends.sort()
830-        if settings.DATABASE_ENGINE not in available_backends:
831-            raise ImproperlyConfigured, "%r isn't an available database backend. Available options are: %s" % \
832-                (settings.DATABASE_ENGINE, ", ".join(map(repr, available_backends)))
833+
834+def connect(settings, name, **kw):
835+    """Connect to the database specified in settings. Returns a
836+    ConnectionInfo on success, raises ImproperlyConfigured if the
837+    settings don't specify a valid database connection.
838+    """
839+    return ConnectionInfo(settings, name, **kw)
840+
841+
842+class ConnectionInfo(object):
843+    """Encapsulates all information about a connection and the backend
844+    to which it belongs. Provides methods for loading backend
845+    creation, introspection, and shell modules, closing the
846+    connection, and resetting the connection's query log.
847+    """
848+    def __init__(self, settings=None, name=_default, **kw):
849+        super(ConnectionInfo, self).__init__(**kw)
850+        if settings is None:
851+            from django.conf import settings
852+        if not settings.DATABASE_OPTIONS:
853+            settings.DATABASE_OPTIONS = {}
854+        self.settings = settings
855+        self.name = name
856+        self.backend = self.load_backend()
857+        self.connection = self.backend.DatabaseWrapper(settings, name)
858+        self.DatabaseError = self.backend.DatabaseError
859+
860+        # Register an event that closes the database connection
861+        # when a Django request is finished.
862+        dispatcher.connect(self.close, signal=signals.request_finished)
863+
864+        # Register an event that resets connection.queries
865+        # when a Django request is started.
866+        dispatcher.connect(self.reset_queries, signal=signals.request_started)
867+
868+    def __repr__(self):
869+        return "Connection: %s, %r (ENGINE=%s NAME=%s)" \
870+               % (self.name, self.connection,
871+                  self.settings.DATABASE_ENGINE,
872+                  self.settings.DATABASE_NAME)
873+
874+    def name(self):
875+        return self.name
876+
877+    def close(self):
878+        """Close connection"""
879+        self.connection.close()
880+
881+    def _import_database_module(self, import_path='', module_name=''):
882+        """Lazily import a database module when requested."""
883+        return __import__('%s%s.%s' % (import_path, self.settings.DATABASE_ENGINE, module_name), {}, {}, [''])
884+
885+    # We don't want to import the introspect module unless someone asks for it, so
886+    # lazily load it on demmand.
887+    def get_introspection_module(self):
888+        return self._import_database_module(self._import_path, 'introspection')
889+
890+    def get_creation_module(self):
891+        return self._import_database_module(self._import_path, 'creation')
892+
893+    def load_backend(self):
894+        try:
895+            # Most of the time, the database backend will be one of the official
896+            # backends that ships with Django, so look there first.
897+            self._import_path = 'django.db.backends.'
898+            backend = __import__('%s%s.base' % (self._import_path,
899+                            self.settings.DATABASE_ENGINE), {}, {}, [''])
900+        except ImportError, e:
901+            # If the import failed, we might be looking for a database backend
902+            # distributed external to Django. So we'll try that next.
903+            try:
904+                _import_path = ''
905+                backend = __import__('%s.base' % self.settings.DATABASE_ENGINE, {}, {}, [''])
906+            except ImportError, e_user:
907+                # The database backend wasn't found. Display a helpful error message
908+                # listing all possible (built-in) database backends.
909+                backend_dir = os.path.join(__path__[0], 'backends')
910+                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')]
911+                available_backends.sort()
912+                if self.settings.DATABASE_ENGINE not in available_backends:
913+                    raise ImproperlyConfigured, "%r isn't an available database backend. Available options are: %s" % \
914+                        (self.settings.DATABASE_ENGINE, ", ".join(map(repr, available_backends)))
915+                else:
916+                    raise # If there's some other error, this must be an error in Django itself.
917+        return backend
918+
919+    # We want runshell() to work the same way, but we have to treat it a
920+    # little differently (since it just runs instead of returning a module like
921+    # the above) and wrap the lazily-loaded runshell() method.
922+    def runshell(self):
923+        return lambda: _import_database_module(_import_path, "client").runshell()
924+
925+    def reset_queries(self):
926+        """Reset log of queries executed by connection"""
927+        self.connection.queries = []
928+
929+class LazyConnectionManager(object):
930+    """Manages named connections lazily, instantiating them as
931+    they are requested.
932+    """
933+    def __init__(self):
934+        self.local = local()
935+        self.local.connections = {}
936+
937+        # Reset connections on request finish, to make sure each request can
938+        # load the correct connections for its settings
939+        dispatcher.connect(self.reset, signal=signals.request_finished)
940+
941+    def __iter__(self):
942+        # Iterates only over *active* connections, not all possible
943+        # connections
944+        return iter(self.local.connections.keys())
945+
946+    def __getattr__(self, attr):
947+        return getattr(self.local.connections, attr)
948+
949+    def __getitem__(self, k):
950+        try:
951+            return self.local.connections[k]
952+        except (AttributeError, KeyError):
953+            return self.connect(k)
954+
955+    def __setitem__(self, k, v):
956+        try:
957+            self.local.connections[k] = v
958+        except AttributeError:
959+            # First access in thread
960+            self.local.connections = {k: v}
961+
962+    def connect(self, name):
963+        """Return the connection with this name in
964+        settings.OTHER_DATABASES. Creates the connection if it doesn't yet
965+        exist. Reconnects if it does. If the name requested is the default
966+        connection (a singleton defined in django.db), then the default
967+        connection is returned.
968+        """
969+        try:
970+            cnx = self.local.connections
971+        except AttributeError:
972+            cnx = self.local.connections = {}
973+        if name in cnx:
974+            cnx[name].close()
975+        if name is _default:
976+            cnx[name] = connect(settings, _default)
977+            return cnx[name]
978+        try:
979+            info = settings.OTHER_DATABASES[name]
980+        except KeyError:
981+            raise ImproperlyConfigured, \
982+                  "No database connection '%s' has been configured" % name
983+        except AttributeError:
984+            raise ImproperlyConfigured, \
985+                  "No OTHER_DATABASES in settings."
986+
987+        # In settings it's a dict, but connect() needs an object:
988+        # pass global settings so that the default connection settings
989+        # can be defaults for the named connections.
990+        database = UserSettingsHolder(settings)
991+        for k, v in info.items():
992+            setattr(database, k, v)
993+        cnx[name] = connect(database, name)
994+        return cnx[name]
995+
996+    def items(self):
997+        # Iterates over *all possible* connections
998+        items = []
999+        for key in self.keys():
1000+            items.append((key, self[key]))
1001+        return items
1002+
1003+    def keys(self):
1004+        # Iterates over *all possible* connections
1005+        keys = [_default]
1006+        try:
1007+            keys.extend(settings.OTHER_DATABASES.keys())
1008+        except AttributeError:
1009+            pass
1010+        return keys
1011+
1012+    def reset(self):
1013+        if not hasattr(self.local, 'connections'):
1014+            return
1015+        if settings.DATABASE_NAME == ':memory:':
1016+##            try:
1017+##                cnx = self.local.connections
1018+##            except AttributeError:
1019+##                cnx = self.local.connections = {}
1020+            self.local.connections = {_default: self.local.connections[_default]}
1021+##            for c in cnx:
1022+##                if c != _default:
1023+##                    del self.local.connections[c]
1024         else:
1025-            raise # If there's some other error, this must be an error in Django itself.
1026+            self.local.connections = {}
1027 
1028-def _import_database_module(import_path='', module_name=''):
1029-    """Lazily import a database module when requested."""
1030-    return __import__('%s%s.%s' % (import_path, settings.DATABASE_ENGINE, module_name), {}, {}, [''])
1031+def model_connection_name(klass):
1032+    """Get the connection name that a model is configured to use, with the
1033+    current settings.
1034+    """
1035+    app = klass._meta.app_label
1036+    model = klass.__name__
1037+    app_model = "%s.%s" % (app, model)
1038 
1039-# We don't want to import the introspect module unless someone asks for it, so
1040-# lazily load it on demmand.
1041-get_introspection_module = curry(_import_database_module, _import_path, 'introspection')
1042+    # Quick exit if no OTHER_DATABASES defined
1043+    if (not hasattr(settings, 'OTHER_DATABASES')
1044+        or not settings.OTHER_DATABASES):
1045+        return _default
1046+    # Look in MODELS for the best match: app_label.Model. If that isn't
1047+    # found, take just app_label. If nothing is found, use the default
1048+    maybe = None
1049+    for name, db_def in settings.OTHER_DATABASES.items():
1050+        if not 'MODELS' in db_def:
1051+            continue
1052+        mods = db_def['MODELS']
1053+        # Can't get a better match than this
1054+        if app_model in mods:
1055+            return name
1056+        elif app in mods:
1057+            if maybe is not None:
1058+                raise ImproperlyConfigured, \
1059+                    "App %s appears in more than one OTHER_DATABASES " \
1060+                    "setting (%s and %s)" % (maybe, name)
1061+            maybe = name
1062+    if maybe:
1063+        return maybe
1064+    # No named connection for this model; use the default
1065+    return _default
1066 
1067-def get_creation_module():
1068-    return creation
1069+def connection_name_app_model(app_label, model_name):
1070+    """Get the connection name that a model is configured to use, with the
1071+    current settings.
1072+    """
1073+    app_model = "%s.%s" % (app_label, model_name)
1074 
1075-# We want runshell() to work the same way, but we have to treat it a
1076-# little differently (since it just runs instead of returning a module like
1077-# the above) and wrap the lazily-loaded runshell() method.
1078-runshell = lambda: _import_database_module(_import_path, "client").runshell()
1079+    # Quick exit if no OTHER_DATABASES defined
1080+    if (not hasattr(settings, 'OTHER_DATABASES')
1081+        or not settings.OTHER_DATABASES):
1082+        return _default
1083+    # Look in MODELS for the best match: app_label.Model. If that isn't
1084+    # found, take just app_label. If nothing is found, use the default
1085+    maybe = None
1086+    for name, db_def in settings.OTHER_DATABASES.items():
1087+        if not 'MODELS' in db_def:
1088+            continue
1089+        mods = db_def['MODELS']
1090+        # Can't get a better match than this
1091+        if app_model in mods:
1092+            return name
1093+        elif app_label in mods:
1094+            if maybe is not None:
1095+                raise ImproperlyConfigured, \
1096+                    "App %s appears in more than one OTHER_DATABASES " \
1097+                    "setting (%s and %s)" % (maybe, name)
1098+            maybe = name
1099+    if maybe:
1100+        return maybe
1101+    # No named connection for this model; use the default
1102+    return _default
1103 
1104-# Convenient aliases for backend bits.
1105-connection = backend.DatabaseWrapper(**settings.DATABASE_OPTIONS)
1106-DatabaseError = backend.DatabaseError
1107-IntegrityError = backend.IntegrityError
1108+class ConnectionInfoDescriptor(object):
1109+    """Descriptor used to access database connection information from a
1110+    manager or other connection holder. Keeps a thread-local cache of
1111+    connections per instance, and always returns the same connection for an
1112+    instance in particular thread during a particular request.
1113 
1114-# Register an event that closes the database connection
1115-# when a Django request is finished.
1116-dispatcher.connect(connection.close, signal=signals.request_finished)
1117+    Any object that includes a ``model`` attribute that holds a model class
1118+    can use this descriptor to manage connections.
1119+    """
1120 
1121-# Register an event that resets connection.queries
1122-# when a Django request is started.
1123+    def __init__(self):
1124+        self.local = local()
1125+        self.local.cnx = {}
1126+        dispatcher.connect(self.reset, signal=signals.request_finished)
1127+
1128+    def __get__(self, instance, type=None):
1129+        if instance is None:
1130+            raise AttributeError, \
1131+                "ConnectionInfo is accessible only through an instance"
1132+        try:
1133+            instance_connection = self.local.cnx.get(instance, None)
1134+        except AttributeError:
1135+            # First access in this thread
1136+            self.local.cnx = {}
1137+            instance_connection = None
1138+        if instance_connection is None:
1139+            instance_connection = self.get_connection(instance)
1140+            self.local.cnx[instance] = instance_connection
1141+        return instance_connection
1142+
1143+    def __set__(self, instance, value):
1144+        try:
1145+            self.local.cnx[instance] = value
1146+        except AttributeError:
1147+            # First access in thread
1148+            self.local.cnx = {instance: value}
1149+
1150+    def __delete__(self, instance):
1151+        try:
1152+            del self.local.cnx[instance]
1153+        except (AttributeError, KeyError):
1154+            # Not stored, no need to reset
1155+            pass
1156+
1157+    def get_connection(self, instance):
1158+        return connections[model_connection_name(instance.model)]
1159+
1160+    def reset(self):
1161+        if not hasattr(self.local, 'cnx'):
1162+            return
1163+        self.local.cnx = {}
1164+
1165+class LocalizingProxy:
1166+    """A lazy-initializing proxy. The proxied object is not
1167+    initialized until the first attempt to access it. This is used to
1168+    attach module-level properties to local storage.
1169+    """
1170+    def __init__(self, name, storage, func, *arg, **kw):
1171+        self.__name = name
1172+        self.__storage = storage
1173+        self.__func = func
1174+        self.__arg = arg
1175+        self.__kw = kw
1176+
1177+        # We need to clear out this thread's storage at the end of each
1178+        # request, in case new settings are loaded with the next
1179+        def reset(stor=storage, name=name):
1180+            if hasattr(stor, name):
1181+                delattr(stor, name)
1182+        dispatcher.connect(reset, signal=signals.request_finished)
1183+
1184+    def __getattr__(self, attr):
1185+        # Private (__*) attributes are munged
1186+        if attr.startswith('_LocalizingProxy'):
1187+            return self.__dict__[attr]
1188+        try:
1189+            return getattr(getattr(self.__storage, self.__name), attr)
1190+        except AttributeError:
1191+            setattr(self.__storage, self.__name, self.__func(*self.__arg,
1192+                                                             **self.__kw))
1193+            return getattr(getattr(self.__storage, self.__name), attr)
1194+
1195+    def __setattr__(self, attr, val):
1196+        # Private (__*) attributes are munged
1197+        if attr.startswith('_LocalizingProxy'):
1198+            self.__dict__[attr] = val
1199+            return
1200+        try:
1201+            stor = getattr(self.__storage, self.__name)
1202+        except AttributeError:
1203+            stor =  self.__func(*self.__arg)
1204+            setattr(self.__storage, self.__name, stor)
1205+        setattr(stor, attr, val)
1206+
1207+
1208+# Create a manager for named connections
1209+connections = LazyConnectionManager()
1210+
1211+# Backwards compatibility: establish the default connection and set the
1212+# default connection properties at module level, using the lazy proxy so that
1213+# each thread may have a different default connection, if so configured
1214+connection_info = LocalizingProxy('connection_info', _local,
1215+                                  lambda: connections[_default])
1216+connection = LocalizingProxy('connection', _local,
1217+                             lambda: connections[_default].connection)
1218+backend = LocalizingProxy('backend', _local,
1219+                          lambda: connections[_default].backend)
1220+DatabaseError = LocalizingProxy('DatabaseError', _local,
1221+                                lambda: connections[_default].DatabaseError)
1222+IntegrityError = LocalizingProxy('IntegrityError', _local,
1223+                                lambda: connections[_default].IntegrityError)
1224+get_introspection_module = LocalizingProxy(
1225+    'get_introspection_module', _local,
1226+    lambda: connections[_default].get_introspection_module)
1227+get_creation_module = LocalizingProxy(
1228+    'get_creation_module', _local,
1229+    lambda: connections[_default].get_creation_module)
1230+runshell = LocalizingProxy('runshell', _local,
1231+                           lambda: connections[_default].runshell)
1232+
1233+
1234 def reset_queries():
1235-    connection.queries = []
1236-dispatcher.connect(reset_queries, signal=signals.request_started)
1237+    connections[_default].reset_queries()
1238+    for c in connections:
1239+        try:
1240+            c.reset_queries()
1241+        except:
1242+            pass
1243 
1244-# Register an event that rolls back the connection
1245+# Register an event that rolls back all connections
1246 # when a Django request has an exception.
1247 def _rollback_on_exception():
1248     from django.db import transaction
1249     transaction.rollback_unless_managed()
1250-dispatcher.connect(_rollback_on_exception, signal=signals.got_request_exception)
1251+dispatcher.connect(_rollback_on_exception,
1252+                   signal=signals.got_request_exception)
1253+
1254=== django/db/backends/sqlite3/base.py
1255==================================================================
1256--- django/db/backends/sqlite3/base.py  (/mirror/django/trunk)  (revision 5420)
1257
1258+++ django/db/backends/sqlite3/base.py  (/local/django/mymultidb)       (revision 5420)
1259
1260@@ -118,7 +118,7 @@
1261
1262         return self.connection.cursor(factory=SQLiteCursorWrapper)
1263 
1264     def close(self):
1265-        from django.conf import settings
1266+        settings = self.settings
1267         # If database is in memory, closing the connection destroys the
1268         # database. To prevent accidental data loss, ignore close requests on
1269         # an in-memory db.
1270=== django/db/backends/__init__.py
1271==================================================================
1272--- django/db/backends/__init__.py      (/mirror/django/trunk)  (revision 5420)
1273
1274+++ django/db/backends/__init__.py      (/local/django/mymultidb)       (revision 5420)
1275
1276@@ -5,15 +5,17 @@
1277
1278     # Import copy of _thread_local.py from Python 2.4
1279     from django.utils._threading_local import local
1280 
1281-class BaseDatabaseWrapper(local):
1282+class BaseDatabaseWrapper(object):
1283     """
1284     Represents a database connection.
1285     """
1286     ops = None
1287-    def __init__(self, **kwargs):
1288+    def __init__(self, settings, connection_name):
1289+        self.settings = settings
1290+        self.connection_name = connection_name
1291+        self.options = settings.DATABASE_OPTIONS
1292         self.connection = None
1293         self.queries = []
1294-        self.options = kwargs
1295 
1296     def _commit(self):
1297         if self.connection is not None:
1298@@ -29,7 +31,7 @@
1299
1300             self.connection = None
1301 
1302     def cursor(self):
1303-        from django.conf import settings
1304+        settings = self.settings
1305         cursor = self._cursor(settings)
1306         if settings.DEBUG:
1307             return self.make_debug_cursor(cursor)
1308=== django/db/transaction.py
1309==================================================================
1310--- django/db/transaction.py    (/mirror/django/trunk)  (revision 5420)
1311
1312+++ django/db/transaction.py    (/local/django/mymultidb)       (revision 5420)
1313
1314@@ -16,7 +16,6 @@
1315
1316     import thread
1317 except ImportError:
1318     import dummy_thread as thread
1319-from django.db import connection
1320 from django.conf import settings
1321 
1322 class TransactionManagementError(Exception):
1323@@ -116,48 +115,69 @@
1324
1325     Puts the transaction manager into a manual state: managed transactions have
1326     to be committed explicitly by the user. If you switch off transaction
1327     management and there is a pending commit/rollback, the data will be
1328-    commited.
1329+    commited. Note that managed state applies across all connections.
1330     """
1331     thread_ident = thread.get_ident()
1332     top = state.get(thread_ident, None)
1333     if top:
1334         top[-1] = flag
1335         if not flag and is_dirty():
1336-            connection._commit()
1337+            for cx in all_connections():
1338+                cx._commit()
1339             set_clean()
1340     else:
1341         raise TransactionManagementError("This code isn't under transaction management")
1342 
1343-def commit_unless_managed():
1344+def commit_unless_managed(conns=None):
1345     """
1346     Commits changes if the system is not in managed transaction mode.
1347     """
1348     if not is_managed():
1349-        connection._commit()
1350+        if conns is None:
1351+            conns = all_connections()
1352+        else:
1353+            conns = ensure_connections(conns)
1354+        for cx in conns:
1355+            cx._commit()
1356     else:
1357         set_dirty()
1358 
1359-def rollback_unless_managed():
1360+def rollback_unless_managed(conns=None):
1361     """
1362     Rolls back changes if the system is not in managed transaction mode.
1363     """
1364     if not is_managed():
1365-        connection._rollback()
1366+        if conns is None:
1367+            conns = all_connections()
1368+        else:
1369+            conns = ensure_connections(conns)
1370+        for cx in conns:
1371+            cx._rollback()
1372     else:
1373         set_dirty()
1374 
1375-def commit():
1376+def commit(conns=None):
1377     """
1378     Does the commit itself and resets the dirty flag.
1379     """
1380-    connection._commit()
1381+    if conns is None:
1382+        conns = all_connections()
1383+    else:
1384+        conns = ensure_connections(conns)
1385+    for cx in conns:
1386+        cx._commit()
1387     set_clean()
1388 
1389-def rollback():
1390+def rollback(conns=None):
1391     """
1392     This function does the rollback itself and resets the dirty flag.
1393     """
1394-    connection._rollback()
1395+    if conns is None:
1396+        conns = all_connections()
1397+    else:
1398+        conns = ensure_connections(conns)
1399+    for cx in conns:
1400+        cx._rollback()
1401     set_clean()
1402 
1403 ##############
1404@@ -179,7 +199,7 @@
1405
1406             leave_transaction_management()
1407     return _autocommit
1408 
1409-def commit_on_success(func):
1410+def commit_on_success(func, conns = None):
1411     """
1412     This decorator activates commit on response. This way, if the view function
1413     runs successfully, a commit is made; if the viewfunc produces an exception,
1414@@ -198,7 +218,7 @@
1415
1416                 raise
1417             else:
1418                 if is_dirty():
1419-                    commit()
1420+                    commit(conns)
1421             return res
1422         finally:
1423             leave_transaction_management()
1424@@ -220,3 +240,31 @@
1425
1426             leave_transaction_management()
1427 
1428     return _commit_manually
1429+
1430+###########
1431+# HELPERS #
1432+###########
1433+
1434+def all_connections():
1435+    from django.db import connection, connections
1436+    return [connection] + [ c.connection
1437+                               for c in connections.values() ]
1438+
1439+def ensure_connections(val):
1440+    from django.db import connections
1441+    conns = []
1442+    if isinstance(val, basestring):
1443+        val = [val]
1444+    try:
1445+        iter(val)
1446+    except:
1447+        val = [val]
1448+    for cx in val:
1449+        if hasattr(cx, 'cursor'):
1450+            conns.append(cx)
1451+        elif hasattr(cx, 'connection'):
1452+            conns.append(cx.connection)
1453+        elif isinstance(cx, basestring):
1454+            conns.append(connections[cx].connection)
1455+    return conns
1456+
1457=== django/conf/global_settings.py
1458==================================================================
1459--- django/conf/global_settings.py      (/mirror/django/trunk)  (revision 5420)
1460
1461+++ django/conf/global_settings.py      (/local/django/mymultidb)       (revision 5420)
1462
1463@@ -122,6 +122,9 @@
1464
1465 DATABASE_PORT = ''             # Set to empty string for default. Not used with sqlite3.
1466 DATABASE_OPTIONS = {}          # Set to empty dictionary for default.
1467 
1468+# Optional named database connections in addition to the default.
1469+OTHER_DATABASES = {}
1470+
1471 # Host for sending e-mail.
1472 EMAIL_HOST = 'localhost'
1473 
1474@@ -352,6 +355,16 @@
1475
1476 # If None, a name of 'test_' + DATABASE_NAME will be assumed
1477 TEST_DATABASE_NAME = None
1478 
1479+# Tuple of other test databases to create. Names in this tuple
1480+# are suffixes that will be appended to TEST_DATABASE_NAME
1481+TEST_DATABASES = []
1482+
1483+# Models to assign to each test database. This must be a list of
1484+# dicts, with each dict key being a name from TEST_DATABASES and value
1485+# a list of models or app_labels that will use that database.
1486+TEST_DATABASE_MODELS = []
1487+
1488+
1489 # Strings used to set the character set and collation order for the test
1490 # database. These values are passed literally to the server, so they are
1491 # backend-dependent. If None, no special settings are sent (system defaults are
1492=== django/core/cache/backends/db.py
1493==================================================================
1494--- django/core/cache/backends/db.py    (/mirror/django/trunk)  (revision 5420)
1495
1496+++ django/core/cache/backends/db.py    (/local/django/mymultidb)       (revision 5420)
1497
1498@@ -1,7 +1,7 @@
1499
1500 "Database cache backend."
1501 
1502 from django.core.cache.backends.base import BaseCache
1503-from django.db import connection, transaction, DatabaseError
1504+from django.db import connections, _default, transaction, DatabaseError
1505 import base64, time
1506 from datetime import datetime
1507 try:
1508@@ -25,7 +25,7 @@
1509
1510             self._cull_frequency = 3
1511 
1512     def get(self, key, default=None):
1513-        cursor = connection.cursor()
1514+        cursor = connections[_default].connection.cursor()
1515         cursor.execute("SELECT cache_key, value, expires FROM %s WHERE cache_key = %%s" % self._table, [key])
1516         row = cursor.fetchone()
1517         if row is None:
1518@@ -46,7 +46,7 @@
1519
1520     def _base_set(self, mode, key, value, timeout=None):
1521         if timeout is None:
1522             timeout = self.default_timeout
1523-        cursor = connection.cursor()
1524+        cursor = connections[_default].connection.cursor()
1525         cursor.execute("SELECT COUNT(*) FROM %s" % self._table)
1526         num = cursor.fetchone()[0]
1527         now = datetime.now().replace(microsecond=0)
1528@@ -67,12 +67,12 @@
1529
1530             transaction.commit_unless_managed()
1531 
1532     def delete(self, key):
1533-        cursor = connection.cursor()
1534+        cursor = connections[_default].connection.cursor()
1535         cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % self._table, [key])
1536         transaction.commit_unless_managed()
1537 
1538     def has_key(self, key):
1539-        cursor = connection.cursor()
1540+        cursor = connections[_default].connection.cursor()
1541         cursor.execute("SELECT cache_key FROM %s WHERE cache_key = %%s" % self._table, [key])
1542         return cursor.fetchone() is not None
1543 
1544=== django/core/management/base.py
1545==================================================================
1546--- django/core/management/base.py      (/mirror/django/trunk)  (revision 5420)
1547
1548+++ django/core/management/base.py      (/local/django/mymultidb)       (revision 5420)
1549
1550@@ -85,14 +85,7 @@
1551
1552                 self.validate()
1553             output = self.handle(*args, **options)
1554             if output:
1555-                if self.output_transaction:
1556-                    # This needs to be imported here, because it relies on settings.
1557-                    from django.db import connection
1558-                    if connection.ops.start_transaction_sql():
1559-                        print self.style.SQL_KEYWORD(connection.ops.start_transaction_sql())
1560                 print output
1561-                if self.output_transaction:
1562-                    print self.style.SQL_KEYWORD("COMMIT;")
1563         except CommandError, e:
1564             sys.stderr.write(self.style.ERROR(str('Error: %s\n' % e)))
1565             sys.exit(1)
1566@@ -124,23 +117,70 @@
1567
1568     args = '<appname appname ...>'
1569 
1570     def handle(self, *app_labels, **options):
1571-        from django.db import models
1572+        from django.db import models, _default, connections
1573+        from django.core.management.sql import sql_collate
1574         if not app_labels:
1575             raise CommandError('Enter at least one appname.')
1576         try:
1577             app_list = [models.get_app(app_label) for app_label in app_labels]
1578         except (ImproperlyConfigured, ImportError), e:
1579             raise CommandError("%s. Are you sure your INSTALLED_APPS setting is correct?" % e)
1580-        output = []
1581+        output = {}
1582         for app in app_list:
1583             app_output = self.handle_app(app, **options)
1584-            if app_output:
1585-                output.append(app_output)
1586-        return '\n'.join(output)
1587+            if isinstance(app_output, dict):
1588+                for connection_name, statements in app_output.items():
1589+                    f_output = output.setdefault(connection_name, [])
1590+                    if self.output_transaction:
1591+                        if connections[connection_name].connection.ops.start_transaction_sql():
1592+                            f_output.append(self.style.SQL_KEYWORD(connections[connection_name].connection.ops.start_transaction_sql()))
1593+                    f_output.extend(statements)
1594+                    if self.output_transaction:
1595+                        f_output.append(self.style.SQL_KEYWORD("COMMIT;"))
1596+            else:
1597+                f_output = output.setdefault(_default, [])
1598+                f_output.append(app_output)
1599 
1600+        return sql_collate(output)
1601+##        return '\n'.join(output)
1602+
1603     def handle_app(self, app, **options):
1604         raise NotImplementedError()
1605 
1606+class ConnectionCommand(BaseCommand):
1607+    args = '<connectionname connectionname ...>'
1608+
1609+    def handle(self, *connection_labels, **options):
1610+        from django.conf import settings
1611+        from django.db import _default, connections
1612+        from django.core.management.sql import sql_collate
1613+        if not connection_labels:
1614+            connection_list = [_default]
1615+            connection_list.extend(settings.OTHER_DATABASES.keys())
1616+        else:
1617+            # add check connection exists
1618+            connection_list = []
1619+            for name in connection_labels:
1620+                if name == '_default':
1621+                    connection_list.append(_default)
1622+                elif name in settings.OTHER_DATABASES:
1623+                    connection_list.append(name)
1624+        output = {}
1625+        for connection_name in connection_list:
1626+            f_output = output.setdefault(connection_name, [])
1627+            connection_output = self.handle_connection(connection_name, **options)
1628+            if connection_output:
1629+                if self.output_transaction:
1630+                    if connections[connection_name].connection.ops.start_transaction_sql():
1631+                        f_output.append(self.style.SQL_KEYWORD(connections[connection_name].connection.ops.start_transaction_sql()))
1632+                f_output.extend(connection_output)
1633+                if self.output_transaction:
1634+                    f_output.append(self.style.SQL_KEYWORD("COMMIT;"))
1635+        return sql_collate(output)
1636+
1637+    def handle_connection(self, connection_name, **options):
1638+        raise NotImplementedError()
1639+
1640 class LabelCommand(BaseCommand):
1641     args = '<label label ...>'
1642     label = 'label'
1643=== django/core/management/commands/sqlall.py
1644==================================================================
1645--- django/core/management/commands/sqlall.py   (/mirror/django/trunk)  (revision 5420)
1646
1647+++ django/core/management/commands/sqlall.py   (/local/django/mymultidb)       (revision 5420)
1648
1649@@ -7,4 +7,4 @@
1650
1651 
1652     def handle_app(self, app, **options):
1653         from django.core.management.sql import sql_all
1654-        return '\n'.join(sql_all(app, self.style))
1655+        return sql_all(app, self.style)
1656=== django/core/management/commands/sqlcustom.py
1657==================================================================
1658--- django/core/management/commands/sqlcustom.py        (/mirror/django/trunk)  (revision 5420)
1659
1660+++ django/core/management/commands/sqlcustom.py        (/local/django/mymultidb)       (revision 5420)
1661
1662@@ -7,4 +7,5 @@
1663
1664 
1665     def handle_app(self, app, **options):
1666         from django.core.management.sql import sql_custom
1667-        return '\n'.join(sql_custom(app))
1668+        return sql_custom(app)
1669+
1670=== django/core/management/commands/sql.py
1671==================================================================
1672--- django/core/management/commands/sql.py      (/mirror/django/trunk)  (revision 5420)
1673
1674+++ django/core/management/commands/sql.py      (/local/django/mymultidb)       (revision 5420)
1675
1676@@ -7,4 +7,4 @@
1677
1678 
1679     def handle_app(self, app, **options):
1680         from django.core.management.sql import sql_create
1681-        return '\n'.join(sql_create(app, self.style))
1682+        return sql_create(app, self.style)
1683=== django/core/management/commands/createcachetable.py
1684==================================================================
1685--- django/core/management/commands/createcachetable.py (/mirror/django/trunk)  (revision 5420)
1686
1687+++ django/core/management/commands/createcachetable.py (/local/django/mymultidb)       (revision 5420)
1688
1689@@ -8,7 +8,7 @@
1690
1691     requires_model_validation = False
1692 
1693     def handle_label(self, tablename, **options):
1694-        from django.db import connection, transaction, models
1695+        from django.db import connections, transaction, models, _default
1696         fields = (
1697             # "key" is a reserved word in MySQL, so use "cache_key" instead.
1698             models.CharField(name='cache_key', max_length=255, unique=True, primary_key=True),
1699@@ -17,9 +17,9 @@
1700
1701         )
1702         table_output = []
1703         index_output = []
1704-        qn = connection.ops.quote_name
1705+        qn = connections[_default].connection.ops.quote_name
1706         for f in fields:
1707-            field_output = [qn(f.name), f.db_type()]
1708+            field_output = [qn(f.name), f.db_type(_default)]
1709             field_output.append("%sNULL" % (not f.null and "NOT " or ""))
1710             if f.unique:
1711                 field_output.append("UNIQUE")
1712@@ -35,7 +35,7 @@
1713
1714         for i, line in enumerate(table_output):
1715             full_statement.append('    %s%s' % (line, i < len(table_output)-1 and ',' or ''))
1716         full_statement.append(');')
1717-        curs = connection.cursor()
1718+        curs = connection[_default].connection.cursor()
1719         curs.execute("\n".join(full_statement))
1720         for statement in index_output:
1721             curs.execute(statement)
1722=== django/core/management/commands/sqlflush.py
1723==================================================================
1724--- django/core/management/commands/sqlflush.py (/mirror/django/trunk)  (revision 5420)
1725
1726+++ django/core/management/commands/sqlflush.py (/local/django/mymultidb)       (revision 5420)
1727
1728@@ -1,10 +1,10 @@
1729
1730-from django.core.management.base import NoArgsCommand
1731+from django.core.management.base import ConnectionCommand
1732 
1733-class Command(NoArgsCommand):
1734+class Command(ConnectionCommand):
1735     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."
1736 
1737     output_transaction = True
1738 
1739-    def handle_noargs(self, **options):
1740+    def handle_connection(self, connection, **options):
1741         from django.core.management.sql import sql_flush
1742-        return '\n'.join(sql_flush(self.style, only_django=True))
1743+        return sql_flush(connection, self.style, only_django=True)
1744=== django/core/management/commands/sqlreset.py
1745==================================================================
1746--- django/core/management/commands/sqlreset.py (/mirror/django/trunk)  (revision 5420)
1747
1748+++ django/core/management/commands/sqlreset.py (/local/django/mymultidb)       (revision 5420)
1749
1750@@ -7,4 +7,4 @@
1751
1752 
1753     def handle_app(self, app, **options):
1754         from django.core.management.sql import sql_reset
1755-        return '\n'.join(sql_reset(app, self.style))
1756+        return sql_reset(app, self.style)
1757=== django/core/management/commands/sqlclear.py
1758==================================================================
1759--- django/core/management/commands/sqlclear.py (/mirror/django/trunk)  (revision 5420)
1760
1761+++ django/core/management/commands/sqlclear.py (/local/django/mymultidb)       (revision 5420)
1762
1763@@ -7,4 +7,4 @@
1764
1765 
1766     def handle_app(self, app, **options):
1767         from django.core.management.sql import sql_delete
1768-        return '\n'.join(sql_delete(app, self.style))
1769+        return sql_delete(app, self.style)
1770=== django/core/management/commands/loaddata.py
1771==================================================================
1772--- django/core/management/commands/loaddata.py (/mirror/django/trunk)  (revision 5420)
1773
1774+++ django/core/management/commands/loaddata.py (/local/django/mymultidb)       (revision 5420)
1775
1776@@ -21,7 +21,9 @@
1777
1778     def handle(self, *fixture_labels, **options):
1779         from django.db.models import get_apps
1780         from django.core import serializers
1781-        from django.db import connection, transaction
1782+        from django.db import connections, model_connection_name, transaction
1783+# MULTIDB: TO REMOVE
1784+        from django.db import connection
1785         from django.conf import settings
1786 
1787         self.style = no_style()
1788@@ -39,7 +41,7 @@
1789
1790         # Get a cursor (even though we don't need one yet). This has
1791         # the side effect of initializing the test database (if
1792         # it isn't already initialized).
1793-        cursor = connection.cursor()
1794+##        cursor = connection.cursor()
1795 
1796         # Start transaction management. All fixtures are installed in a
1797         # single transaction to ensure that all references are resolved.
1798@@ -121,6 +123,7 @@
1799
1800                             print "No %s fixture '%s' in %s." % \
1801                                 (format, fixture_name, humanize(fixture_dir))
1802 
1803+# MULTIDB TODO: use sql_sequence_reset
1804         if object_count > 0:
1805             sequence_sql = connection.ops.sequence_reset_sql(self.style, models)
1806             if sequence_sql:
1807=== django/core/management/commands/inspectdb.py
1808==================================================================
1809--- django/core/management/commands/inspectdb.py        (/mirror/django/trunk)  (revision 5420)
1810
1811+++ django/core/management/commands/inspectdb.py        (/local/django/mymultidb)       (revision 5420)
1812
1813@@ -1,22 +1,23 @@
1814
1815-from django.core.management.base import NoArgsCommand, CommandError
1816+from django.core.management.base import ConnectionCommand, CommandError
1817 
1818-class Command(NoArgsCommand):
1819+class Command(ConnectionCommand):
1820     help = "Introspects the database tables in the given database and outputs a Django model module."
1821 
1822     requires_model_validation = False
1823 
1824-    def handle_noargs(self, **options):
1825+    def handle_connection(self, connection, **options):
1826         try:
1827-            for line in self.handle_inspection():
1828+            for line in self.handle_inspection(connection):
1829                 print line
1830         except NotImplementedError:
1831             raise CommandError("Database inspection isn't supported for the currently selected database backend.")
1832 
1833-    def handle_inspection(self):
1834-        from django.db import connection, get_introspection_module
1835+    def handle_inspection(self, conn):
1836+        from django.db import connections, get_introspection_module
1837         import keyword
1838 
1839-        introspection_module = get_introspection_module()
1840+        connection = connections[conn].connection
1841+        introspection_module = connections[conn].get_introspection_module()
1842 
1843         table2model = lambda table_name: table_name.title().replace('_', '')
1844 
1845=== django/core/management/commands/sqlindexes.py
1846==================================================================
1847--- django/core/management/commands/sqlindexes.py       (/mirror/django/trunk)  (revision 5420)
1848
1849+++ django/core/management/commands/sqlindexes.py       (/local/django/mymultidb)       (revision 5420)
1850
1851@@ -7,4 +7,4 @@
1852
1853 
1854     def handle_app(self, app, **options):
1855         from django.core.management.sql import sql_indexes
1856-        return '\n'.join(sql_indexes(app, self.style))
1857+        return sql_indexes(app, self.style)
1858=== django/core/management/commands/flush.py
1859==================================================================
1860--- django/core/management/commands/flush.py    (/mirror/django/trunk)  (revision 5420)
1861
1862+++ django/core/management/commands/flush.py    (/local/django/mymultidb)       (revision 5420)
1863
1864@@ -1,9 +1,9 @@
1865
1866-from django.core.management.base import NoArgsCommand, CommandError
1867+from django.core.management.base import ConnectionCommand, CommandError
1868 from django.core.management.color import no_style
1869 from optparse import make_option
1870 
1871-class Command(NoArgsCommand):
1872-    option_list = NoArgsCommand.option_list + (
1873+class Command(ConnectionCommand):
1874+    option_list = ConnectionCommand.option_list + (
1875         make_option('--verbosity', action='store', dest='verbosity', default='1',
1876             type='choice', choices=['0', '1', '2'],
1877             help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'),
1878@@ -12,9 +12,9 @@
1879
1880     )
1881     help = "Executes ``sqlflush`` on the current database."
1882 
1883-    def handle_noargs(self, **options):
1884+    def handle_connection(self, connection, **options):
1885         from django.conf import settings
1886-        from django.db import connection, transaction, models
1887+        from django.db import transaction, models, connections
1888         from django.dispatch import dispatcher
1889         from django.core.management.sql import sql_flush, emit_post_sync_signal
1890 
1891@@ -31,7 +31,7 @@
1892
1893             except ImportError:
1894                 pass
1895 
1896-        sql_list = sql_flush(self.style, only_django=True)
1897+        sql_list = sql_flush(connection, self.style, only_django=True)
1898 
1899         if interactive:
1900             confirm = raw_input("""You have requested a flush of the database.
1901@@ -39,14 +39,14 @@
1902
1903 and return each table to the state it was in after syncdb.
1904 Are you sure you want to do this?
1905 
1906-    Type 'yes' to continue, or 'no' to cancel: """ % settings.DATABASE_NAME)
1907+    Type 'yes' to continue, or 'no' to cancel: """ % connection)
1908         else:
1909             confirm = 'yes'
1910 
1911         if confirm == 'yes':
1912             try:
1913-                cursor = connection.cursor()
1914                 for sql in sql_list:
1915+                    cursor = connections[connection].connection.cursor()
1916                     cursor.execute(sql)
1917             except Exception, e:
1918                 transaction.rollback_unless_managed()
1919@@ -55,7 +55,7 @@
1920
1921       * At least one of the expected database tables doesn't exist.
1922       * The SQL was invalid.
1923     Hint: Look at the output of 'django-admin.py sqlflush'. That's the SQL this command wasn't able to run.
1924-    The full error: %s""" % (settings.DATABASE_NAME, e))
1925+    The full error: %s""" % (connection, e))
1926             transaction.commit_unless_managed()
1927 
1928             # Emit the post sync signal. This allows individual
1929=== django/core/management/commands/reset.py
1930==================================================================
1931--- django/core/management/commands/reset.py    (/mirror/django/trunk)  (revision 5420)
1932
1933+++ django/core/management/commands/reset.py    (/local/django/mymultidb)       (revision 5420)
1934
1935@@ -10,10 +10,8 @@
1936
1937     help = "Executes ``sqlreset`` for the given app(s) in the current database."
1938     args = '[appname ...]'
1939 
1940-    output_transaction = True
1941-
1942     def handle_app(self, app, **options):
1943-        from django.db import connection, transaction
1944+        from django.db import connections, transaction, _default
1945         from django.conf import settings
1946         from django.core.management.sql import sql_reset
1947 
1948@@ -21,32 +19,37 @@
1949
1950 
1951         self.style = no_style()
1952 
1953-        sql_list = sql_reset(app, self.style)
1954+        sql_dict = sql_reset(app, self.style)
1955 
1956-        if options.get('interactive'):
1957-            confirm = raw_input("""
1958+        for connection_name, sql_list in sql_dict.items():
1959+            if options.get('interactive'):
1960+                if connection_name == _default:
1961+                    dbname = settings.DATABASE_NAME
1962+                else:
1963+                    dbname = settings.OTHER_DATABASES[connection_name][DATABASE_NAME]
1964+                confirm = raw_input("""
1965 You have requested a database reset.
1966 This will IRREVERSIBLY DESTROY any data for
1967 the "%s" application in the database "%s".
1968 Are you sure you want to do this?
1969 
1970-Type 'yes' to continue, or 'no' to cancel: """ % (app_name, settings.DATABASE_NAME))
1971-        else:
1972-            confirm = 'yes'
1973+Type 'yes' to continue, or 'no' to cancel: """ % (app_name, dbname))
1974+            else:
1975+                confirm = 'yes'
1976 
1977-        if confirm == 'yes':
1978-            try:
1979-                cursor = connection.cursor()
1980-                for sql in sql_list:
1981-                    cursor.execute(sql)
1982-            except Exception, e:
1983-                transaction.rollback_unless_managed()
1984-                raise CommandError("""Error: %s couldn't be reset. Possible reasons:
1985+            if confirm == 'yes':
1986+                try:
1987+                    cursor = connections[connection_name].connection.cursor()
1988+                    for sql in sql_list:
1989+                        cursor.execute(sql)
1990+                except Exception, e:
1991+                    transaction.rollback_unless_managed()
1992+                    raise CommandError("""Error: %s couldn't be reset. Possible reasons:
1993   * The database isn't running or isn't configured correctly.
1994   * At least one of the database tables doesn't exist.
1995   * The SQL was invalid.
1996 Hint: Look at the output of 'django-admin.py sqlreset %s'. That's the SQL this command wasn't able to run.
1997 The full error: %s""" % (app_name, app_name, e))
1998-            transaction.commit_unless_managed()
1999-        else:
2000-            print "Reset cancelled."
2001+                transaction.commit_unless_managed()
2002+            else:
2003+                print "Reset cancelled."
2004=== django/core/management/commands/syncdb.py
2005==================================================================
2006--- django/core/management/commands/syncdb.py   (/mirror/django/trunk)  (revision 5420)
2007
2008+++ django/core/management/commands/syncdb.py   (/local/django/mymultidb)       (revision 5420)
2009
2010@@ -19,7 +19,7 @@
2011
2012     help = "Create the database tables for all apps in INSTALLED_APPS whose tables haven't already been created."
2013 
2014     def handle_noargs(self, **options):
2015-        from django.db import connection, transaction, models
2016+        from django.db import model_connection_name, transaction, models, connections
2017         from django.conf import settings
2018         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
2019 
2020@@ -37,16 +37,10 @@
2021
2022                 if not exc.args[0].startswith('No module named management'):
2023                     raise
2024 
2025-        cursor = connection.cursor()
2026-
2027-        if connection.features.uses_case_insensitive_names:
2028-            table_name_converter = lambda x: x.upper()
2029-        else:
2030-            table_name_converter = lambda x: x
2031         # Get a list of all existing database tables, so we know what needs to
2032         # be added.
2033-        tables = [table_name_converter(name) for name in table_list()]
2034-
2035+        tables = table_list()
2036+       
2037         # Get a list of already installed *models* so that references work right.
2038         seen_models = installed_models(tables)
2039         created_models = set()
2040@@ -59,8 +53,15 @@
2041
2042             for model in model_list:
2043                 # Create the model's database table, if it doesn't already exist.
2044                 if verbosity >= 2:
2045-                    print "Processing %s.%s model" % (app_name, model._meta.object_name)
2046-                if table_name_converter(model._meta.db_table) in tables:
2047+                    print "Processing %s.%s model (%s))" % (app_name, model._meta.object_name, model_connection_name(model))
2048+                connection = model._default_manager.db.connection
2049+                cursor = connection.cursor()
2050+                if connection.features.uses_case_insensitive_names:
2051+                    table_name_converter = lambda x: x.upper()
2052+                else:
2053+                    table_name_converter = lambda x: x
2054+                ctables = [table_name_converter(name) for name in tables]
2055+                if table_name_converter(model._meta.db_table) in ctables:
2056                     continue
2057                 sql, references = sql_model_create(model, self.style, seen_models)
2058                 seen_models.add(model)
2059@@ -83,6 +84,8 @@
2060
2061             model_list = models.get_models(app)
2062             for model in model_list:
2063                 if model in created_models:
2064+                    connection = model._default_manager.db.connection
2065+                    cursor = connection.cursor()
2066                     sql = many_to_many_sql_for_model(model, self.style)
2067                     if sql:
2068                         if verbosity >= 2:
2069@@ -102,6 +105,8 @@
2070
2071             app_name = app.__name__.split('.')[-2]
2072             for model in models.get_models(app):
2073                 if model in created_models:
2074+                    connection = model._default_manager.db.connection
2075+                    cursor = connection.cursor()
2076                     custom_sql = custom_sql_for_model(model)
2077                     if custom_sql:
2078                         if verbosity >= 1:
2079@@ -121,6 +126,8 @@
2080
2081             app_name = app.__name__.split('.')[-2]
2082             for model in models.get_models(app):
2083                 if model in created_models:
2084+                    connection = model._default_manager.db.connection
2085+                    cursor = connection.cursor()
2086                     index_sql = sql_indexes_for_model(model, self.style)
2087                     if index_sql:
2088                         if verbosity >= 1:
2089=== django/core/management/validation.py
2090==================================================================
2091--- django/core/management/validation.py        (/mirror/django/trunk)  (revision 5420)
2092
2093+++ django/core/management/validation.py        (/local/django/mymultidb)       (revision 5420)
2094
2095@@ -19,7 +19,7 @@
2096
2097     Returns number of errors.
2098     """
2099     from django.conf import settings
2100-    from django.db import models, connection
2101+    from django.db import connections, models, model_connection_name
2102     from django.db.models.loading import get_app_errors
2103     from django.db.models.fields.related import RelatedObject
2104 
2105@@ -30,6 +30,8 @@
2106
2107 
2108     for cls in models.get_models(app):
2109         opts = cls._meta
2110+        connection_name = model_connection_name(cls)
2111+        connection = connections[connection_name].connection
2112 
2113         # Do field-specific validation.
2114         for f in opts.local_fields:
2115@@ -74,6 +76,12 @@
2116
2117             if f.rel:
2118                 if f.rel.to not in models.get_models():
2119                     e.add(opts, "'%s' has relation with model %s, which has not been installed" % (f.name, f.rel.to))
2120+
2121+#MULTIDB TODO: Fix this to allow relations that span databases by splitting querys up
2122+                rel_connection = model_connection_name(f.rel.to)
2123+                if rel_connection != connection_name:
2124+                    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))
2125+
2126                 # it is a string and we could not find the model it refers to
2127                 # so skip the next section
2128                 if isinstance(f.rel.to, (str, unicode)):
2129=== django/core/management/sql.py
2130==================================================================
2131--- django/core/management/sql.py       (/mirror/django/trunk)  (revision 5420)
2132
2133+++ django/core/management/sql.py       (/local/django/mymultidb)       (revision 5420)
2134
2135@@ -8,11 +8,21 @@
2136
2137     from sets import Set as set   # Python 2.3 fallback
2138 
2139 def table_list():
2140-    "Returns a list of all table names that exist in the database."
2141-    from django.db import connection, get_introspection_module
2142-    cursor = connection.cursor()
2143-    return get_introspection_module().get_table_list(cursor)
2144+    """Returns a list of all table names that exist in the database."""
2145+    from django.db import connections
2146+    table_list = []
2147+    for conn in connections:
2148+        table_list.extend(table_list_conn(conn))
2149+    return table_list
2150 
2151+def table_list_conn(conn):
2152+    from django.db import connections
2153+    try:
2154+        cursor = connections[conn].connection.cursor()
2155+        return connections[conn].get_introspection_module().get_table_list(cursor)
2156+    except:
2157+        return []
2158+
2159 def django_table_list(only_existing=False):
2160     """
2161     Returns a list of all table names that have associated Django models and
2162@@ -32,18 +42,34 @@
2163
2164         tables = [t for t in tables if t in existing]
2165     return tables
2166 
2167+def django_table_list_conn(conn, only_existing=False):
2168+    from django.db import models
2169+    from django.db import model_connection_name
2170+    tables = []
2171+    for app in models.get_apps():
2172+        for model in models.get_models(app):
2173+            if model_connection_name(model)==conn:
2174+                tables.append(model._meta.db_table)
2175+                tables.extend([f.m2m_db_table() for f in model._meta.many_to_many])
2176+    if only_existing:
2177+        existing = table_list_conn(conn)
2178+        tables = [t for t in tables if t in existing]
2179+    return tables
2180+
2181 def installed_models(table_list):
2182     "Returns a set of all models that are installed, given a list of existing table names."
2183-    from django.db import connection, models
2184+    from django.db import models
2185     all_models = []
2186     for app in models.get_apps():
2187         for model in models.get_models(app):
2188-            all_models.append(model)
2189-    if connection.features.uses_case_insensitive_names:
2190-        converter = lambda x: x.upper()
2191-    else:
2192-        converter = lambda x: x
2193-    return set([m for m in all_models if converter(m._meta.db_table) in map(converter, table_list)])
2194+            connection = model._default_manager.db.connection
2195+            if connection.features.uses_case_insensitive_names:
2196+                converter = lambda x: x.upper()
2197+            else:
2198+                converter = lambda x: x
2199+            if converter(model._meta.db_table) in map(converter, table_list):
2200+                all_models.append(converter(model))
2201+    return set(all_models)
2202 
2203 def sequence_list():
2204     "Returns a list of information about all DB sequences for all models in all apps."
2205@@ -66,7 +92,7 @@
2206
2207 
2208 def sql_create(app, style):
2209     "Returns a list of the CREATE TABLE SQL statements for the given app."
2210-    from django.db import models
2211+    from django.db import models, model_connection_name, _default
2212     from django.conf import settings
2213 
2214     if settings.DATABASE_ENGINE == 'dummy':
2215@@ -81,71 +107,79 @@
2216
2217     # generate invalid SQL (leaving models out of known_models is harmless, so
2218     # we can be conservative).
2219     app_models = models.get_models(app)
2220-    final_output = []
2221     known_models = set([model for model in installed_models(table_list()) if model not in app_models])
2222     pending_references = {}
2223 
2224+    connection_output = {}
2225+   
2226     for model in app_models:
2227+        connection_name = model_connection_name(model)
2228+        f_output = connection_output.setdefault(connection_name, [])
2229         output, references = sql_model_create(model, style, known_models)
2230-        final_output.extend(output)
2231+        f_output.extend(output)
2232         for refto, refs in references.items():
2233             pending_references.setdefault(refto, []).extend(refs)
2234             if refto in known_models:
2235-                final_output.extend(sql_for_pending_references(refto, style, pending_references))
2236-        final_output.extend(sql_for_pending_references(model, style, pending_references))
2237+                f_output.extend(sql_for_pending_references(refto, style, pending_references))
2238+        f_output.extend(sql_for_pending_references(model, style, pending_references))
2239         # Keep track of the fact that we've created the table for this model.
2240         known_models.add(model)
2241 
2242     # Create the many-to-many join tables.
2243     for model in app_models:
2244-        final_output.extend(many_to_many_sql_for_model(model, style))
2245+        connection_name = model_connection_name(model)
2246+        f_output = connection_output.setdefault(connection_name, [])
2247+        f_output.extend(many_to_many_sql_for_model(model,style))
2248 
2249+
2250     # Handle references to tables that are from other apps
2251     # but don't exist physically.
2252     not_installed_models = set(pending_references.keys())
2253     if not_installed_models:
2254+        f_output = connection_output.setdefault(_default, [])
2255         alter_sql = []
2256         for model in not_installed_models:
2257             alter_sql.extend(['-- ' + sql for sql in
2258                 sql_for_pending_references(model, style, pending_references)])
2259         if alter_sql:
2260-            final_output.append('-- The following references should be added but depend on non-existent tables:')
2261-            final_output.extend(alter_sql)
2262+            f_output.append('-- The following references should be added but depend on non-existent tables:')
2263+            f_output.extend(alter_sql)
2264 
2265-    return final_output
2266+    return connection_output
2267 
2268 def sql_delete(app, style):
2269     "Returns a list of the DROP TABLE SQL statements for the given app."
2270-    from django.db import connection, models, get_introspection_module
2271+    from django.db import models, model_connection_name
2272     from django.db.backends.util import truncate_name
2273     from django.contrib.contenttypes import generic
2274-    introspection = get_introspection_module()
2275 
2276-    # This should work even if a connection isn't available
2277-    try:
2278-        cursor = connection.cursor()
2279-    except:
2280-        cursor = None
2281+    connection_output = {}
2282 
2283-    # Figure out which tables already exist
2284-    if cursor:
2285-        table_names = introspection.get_table_list(cursor)
2286-    else:
2287-        table_names = []
2288-    if connection.features.uses_case_insensitive_names:
2289-        table_name_converter = lambda x: x.upper()
2290-    else:
2291-        table_name_converter = lambda x: x
2292-
2293-    output = []
2294-    qn = connection.ops.quote_name
2295-
2296     # Output DROP TABLE statements for standard application tables.
2297     to_delete = set()
2298 
2299     references_to_delete = {}
2300     app_models = models.get_models(app)
2301     for model in app_models:
2302+        connection = model._default_manager.db.connection
2303+        introspection = model._default_manager.db.get_introspection_module()
2304+        # This should work even if a connection isn't available
2305+        try:
2306+            cursor = connection.cursor()
2307+        except:
2308+            cursor = None
2309+        # Figure out which tables already exist
2310+        if cursor:
2311+            table_names = introspection.get_table_list(cursor)
2312+        else:
2313+            table_names = []
2314+        if connection.features.uses_case_insensitive_names:
2315+            table_name_converter = lambda x: x.upper()
2316+        else:
2317+            table_name_converter = lambda x: x
2318+
2319+        qn = connection.ops.quote_name
2320+
2321         if cursor and table_name_converter(model._meta.db_table) in table_names:
2322             # The table exists, so it needs to be dropped
2323             opts = model._meta
2324@@ -156,6 +190,29 @@
2325
2326             to_delete.add(model)
2327 
2328     for model in app_models:
2329+        connection = model._default_manager.db.connection
2330+        connection_name = model_connection_name(model)
2331+        introspection = model._default_manager.db.get_introspection_module()
2332+
2333+        output = connection_output.setdefault(connection_name, [])
2334+
2335+        # This should work even if a connection isn't available
2336+        try:
2337+            cursor = connection.cursor()
2338+        except:
2339+            cursor = None
2340+        # Figure out which tables already exist
2341+        if cursor:
2342+            table_names = introspection.get_table_list(cursor)
2343+        else:
2344+            table_names = []
2345+        if connection.features.uses_case_insensitive_names:
2346+            table_name_converter = str.upper
2347+        else:
2348+            table_name_converter = lambda x: x
2349+
2350+        qn = connection.ops.quote_name
2351+
2352         if cursor and table_name_converter(model._meta.db_table) in table_names:
2353             # Drop the table now
2354             output.append('%s %s;' % (style.SQL_KEYWORD('DROP TABLE'),
2355@@ -180,6 +237,29 @@
2356
2357 
2358     # Output DROP TABLE statements for many-to-many tables.
2359     for model in app_models:
2360+        connection = model._default_manager.db.connection
2361+        connection_name = model_connection_name(model)
2362+        introspection = model._default_manager.db.get_introspection_module()
2363+
2364+        output = connection_output.setdefault(connection_name, [])
2365+
2366+        # This should work even if a connection isn't available
2367+        try:
2368+            cursor = connection.cursor()
2369+        except:
2370+            cursor = None
2371+        # Figure out which tables already exist
2372+        if cursor:
2373+            table_names = introspection.get_table_list(cursor)
2374+        else:
2375+            table_names = []
2376+        if connection.features.uses_case_insensitive_names:
2377+            table_name_converter = str.upper
2378+        else:
2379+            table_name_converter = lambda x: x
2380+
2381+        qn = connection.ops.quote_name
2382+
2383         opts = model._meta
2384         for f in opts.local_many_to_many:
2385             if isinstance(f.rel, generic.GenericRel):
2386@@ -195,71 +275,106 @@
2387
2388 
2389     # Close database connection explicitly, in case this output is being piped
2390     # directly into a database client, to avoid locking issues.
2391-    if cursor:
2392-        cursor.close()
2393-        connection.close()
2394+    for model in app_models:
2395+        connection = model._default_manager.db.connection
2396+        try:
2397+            cursor = connection.cursor()
2398+        except:
2399+            cursor = None
2400+        if cursor:
2401+            cursor.close()
2402+            connection.close()
2403 
2404-    return output[::-1] # Reverse it, to deal with table dependencies.
2405+    return connection_output
2406 
2407 def sql_reset(app, style):
2408     "Returns a list of the DROP TABLE SQL, then the CREATE TABLE SQL, for the given module."
2409-    return sql_delete(app, style) + sql_all(app, style)
2410+    connection_output = {}
2411+    sql_dict = sql_delete(app, style)
2412+    for connection_name, sql_list in sql_dict.items():
2413+        output = connection_output.setdefault(connection_name, [])
2414+        output.extend(sql_list)
2415+    sql_dict = sql_all(app, style)
2416+    for connection_name, sql_list in sql_dict.items():
2417+        output = connection_output.setdefault(connection_name, [])
2418+        output.extend(sql_list)
2419+    return connection_output
2420 
2421-def sql_flush(style, only_django=False):
2422+def sql_flush(connection, style, only_django=False):
2423     """
2424     Returns a list of the SQL statements used to flush the database.
2425     
2426     If only_django is True, then only table names that have associated Django
2427     models and are in INSTALLED_APPS will be included.
2428     """
2429-    from django.db import connection
2430+    from django.db import connections
2431     if only_django:
2432-        tables = django_table_list()
2433+        tables = django_table_list_conn(connection)
2434     else:
2435-        tables = table_list()
2436-    statements = connection.ops.sql_flush(style, tables, sequence_list())
2437-    return statements
2438+        tables = table_list_conn(connection)
2439+    return [f for f in connections[connection].connection.ops.sql_flush(style, tables, sequence_list())]
2440 
2441 def sql_custom(app):
2442     "Returns a list of the custom table modifying SQL statements for the given app."
2443     from django.db.models import get_models
2444-    output = []
2445+    from django.db import model_connection_name
2446+    connection_output = {}
2447 
2448     app_models = get_models(app)
2449     app_dir = os.path.normpath(os.path.join(os.path.dirname(app.__file__), 'sql'))
2450 
2451     for model in app_models:
2452-        output.extend(custom_sql_for_model(model))
2453+        connection_name = model_connection_name(model)
2454+        output = connection_output.setdefault(connection_name, [])
2455+        output.extend(map(str, custom_sql_for_model(model)))
2456+    return connection_output
2457 
2458-    return output
2459-
2460 def sql_indexes(app, style):
2461     "Returns a list of the CREATE INDEX SQL statements for all models in the given app."
2462-    from django.db import models
2463-    output = []
2464-    for model in models.get_models(app):
2465-        output.extend(sql_indexes_for_model(model, style))
2466-    return output
2467+    from django.db import model_connection_name
2468+    from django.db.models import get_models
2469+    connection_output = {}
2470+    for model in get_models(app):
2471+        opts = model._meta
2472+        connection_name = model_connection_name(model)
2473+        output = connection_output.setdefault(connection_name, [])
2474+        output.extend(map(str, sql_indexes_for_model(model, style)))
2475+    return connection_output
2476 
2477 def sql_all(app, style):
2478     "Returns a list of CREATE TABLE SQL, initial-data inserts, and CREATE INDEX SQL for the given module."
2479-    return sql_create(app, style) + sql_custom(app) + sql_indexes(app, style)
2480+    connection_output = {}
2481+    sql_dict = sql_create(app, style)
2482+    for connection_name, sql_list in sql_dict.items():
2483+        output = connection_output.setdefault(connection_name, [])
2484+        output.extend(sql_list)
2485+    sql_dict = sql_custom(app)
2486+    for connection_name, sql_list in sql_dict.items():
2487+        output = connection_output.setdefault(connection_name, [])
2488+        output.extend(sql_list)
2489+    sql_dict = sql_indexes(app, style)
2490+    for connection_name, sql_list in sql_dict.items():
2491+        output = connection_output.setdefault(connection_name, [])
2492+        output.extend(sql_list)
2493+    return connection_output
2494 
2495 def sql_model_create(model, style, known_models=set()):
2496     """
2497     Returns the SQL required to create a single model, as a tuple of:
2498         (list_of_sql, pending_references_dict)
2499     """
2500-    from django.db import connection, models
2501+    from django.db import models, model_connection_name, connections
2502 
2503     opts = model._meta
2504     final_output = []
2505     table_output = []
2506     pending_references = {}
2507+    connection_name = model_connection_name(model)
2508+    connection = connections[connection_name].connection
2509     qn = connection.ops.quote_name
2510     inline_references = connection.features.inline_fk_references
2511     for f in opts.local_fields:
2512-        col_type = f.db_type()
2513+        col_type = f.db_type(connection_name)
2514         tablespace = f.db_tablespace or opts.db_tablespace
2515         if col_type is None:
2516             # Skip ManyToManyFields, because they're not represented as
2517@@ -291,7 +406,7 @@
2518
2519         table_output.append(' '.join(field_output))
2520     if opts.order_with_respect_to:
2521         table_output.append(style.SQL_FIELD(qn('_order')) + ' ' + \
2522-            style.SQL_COLTYPE(models.IntegerField().db_type()) + ' ' + \
2523+            style.SQL_COLTYPE(models.IntegerField().db_type(connection_name)) + ' ' + \
2524             style.SQL_KEYWORD('NULL'))
2525     for field_constraints in opts.unique_together:
2526         table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \
2527@@ -320,10 +435,16 @@
2528
2529     """
2530     Returns any ALTER TABLE statements to add constraints after the fact.
2531     """
2532-    from django.db import connection
2533+    from django.db import connections, model_connection_name
2534     from django.db.backends.util import truncate_name
2535 
2536+    connection_name = model_connection_name(model)
2537+    connection = connections[connection_name].connection
2538     qn = connection.ops.quote_name
2539+##    if hasattr(connection.ops, 'max_constraint_length'):
2540+##        mnl = connection.ops.max_constraint_length
2541+##    else:
2542+##        mnl = connection.ops.max_name_length
2543     final_output = []
2544     if connection.features.supports_constraints:
2545         opts = model._meta
2546@@ -345,12 +466,14 @@
2547
2548     return final_output
2549 
2550 def many_to_many_sql_for_model(model, style):
2551-    from django.db import connection, models
2552+    from django.db import models, connections, model_connection_name
2553     from django.contrib.contenttypes import generic
2554     from django.db.backends.util import truncate_name
2555 
2556     opts = model._meta
2557     final_output = []
2558+    connection_name = model_connection_name(model)
2559+    connection = connections[connection_name].connection
2560     qn = connection.ops.quote_name
2561     inline_references = connection.features.inline_fk_references
2562     for f in opts.local_many_to_many:
2563@@ -364,21 +487,21 @@
2564
2565                 style.SQL_TABLE(qn(f.m2m_db_table())) + ' (']
2566             table_output.append('    %s %s %s%s,' %
2567                 (style.SQL_FIELD(qn('id')),
2568-                style.SQL_COLTYPE(models.AutoField(primary_key=True).db_type()),
2569+                style.SQL_COLTYPE(models.AutoField(primary_key=True).db_type(connection_name)),
2570                 style.SQL_KEYWORD('NOT NULL PRIMARY KEY'),
2571                 tablespace_sql))
2572             if inline_references:
2573                 deferred = []
2574                 table_output.append('    %s %s %s %s (%s)%s,' %
2575                     (style.SQL_FIELD(qn(f.m2m_column_name())),
2576-                    style.SQL_COLTYPE(models.ForeignKey(model).db_type()),
2577+                    style.SQL_COLTYPE(models.ForeignKey(model).db_type(connection_name)),
2578                     style.SQL_KEYWORD('NOT NULL REFERENCES'),
2579                     style.SQL_TABLE(qn(opts.db_table)),
2580                     style.SQL_FIELD(qn(opts.pk.column)),
2581                     connection.ops.deferrable_sql()))
2582                 table_output.append('    %s %s %s %s (%s)%s,' %
2583                     (style.SQL_FIELD(qn(f.m2m_reverse_name())),
2584-                    style.SQL_COLTYPE(models.ForeignKey(f.rel.to).db_type()),
2585+                    style.SQL_COLTYPE(models.ForeignKey(f.rel.to).db_type(connection_name)),
2586                     style.SQL_KEYWORD('NOT NULL REFERENCES'),
2587                     style.SQL_TABLE(qn(f.rel.to._meta.db_table)),
2588                     style.SQL_FIELD(qn(f.rel.to._meta.pk.column)),
2589@@ -386,11 +509,11 @@
2590
2591             else:
2592                 table_output.append('    %s %s %s,' %
2593                     (style.SQL_FIELD(qn(f.m2m_column_name())),
2594-                    style.SQL_COLTYPE(models.ForeignKey(model).db_type()),
2595+                    style.SQL_COLTYPE(models.ForeignKey(model).db_type(connection_name)),
2596                     style.SQL_KEYWORD('NOT NULL')))
2597                 table_output.append('    %s %s %s,' %
2598                     (style.SQL_FIELD(qn(f.m2m_reverse_name())),
2599-                    style.SQL_COLTYPE(models.ForeignKey(f.rel.to).db_type()),
2600+                    style.SQL_COLTYPE(models.ForeignKey(f.rel.to).db_type(connection_name)),
2601                     style.SQL_KEYWORD('NOT NULL')))
2602                 deferred = [
2603                     (f.m2m_db_table(), f.m2m_column_name(), opts.db_table,
2604@@ -456,9 +579,9 @@
2605
2606 
2607 def sql_indexes_for_model(model, style):
2608     "Returns the CREATE INDEX SQL statements for a single model"
2609-    from django.db import connection
2610     output = []
2611 
2612+    connection = model._default_manager.db.connection
2613     qn = connection.ops.quote_name
2614     for f in model._meta.local_fields:
2615         if f.db_index and not ((f.primary_key or f.unique) and connection.features.autoindexes_primary_keys):
2616@@ -489,3 +612,24 @@
2617
2618         dispatcher.send(signal=models.signals.post_syncdb, sender=app,
2619             app=app, created_models=created_models,
2620             verbosity=verbosity, interactive=interactive)
2621+
2622+def sql_collate(connection_output, reverse=False):
2623+    from django.db import _default
2624+    final_output = []
2625+    if len(connection_output.keys()) == 1:
2626+        # all for the default connection
2627+        for statements in connection_output.values():
2628+            final_output.extend(statements)
2629+            if reverse:
2630+                final_output.reverse()
2631+    else:
2632+        for connection_name, statements in connection_output.items():
2633+            if not statements:
2634+                continue
2635+            final_output.append(' -- The following statements are for connection: %s' % connection_name)
2636+            if reverse:
2637+                statements.reverse()
2638+            final_output.extend(statements)
2639+            final_output.append(' -- END statements for %s\n' %
2640+                                connection_name)
2641+    return '\n'.join(map(str, final_output))
2642=== django/core/context_processors.py
2643==================================================================
2644--- django/core/context_processors.py   (/mirror/django/trunk)  (revision 5420)
2645
2646+++ django/core/context_processors.py   (/local/django/mymultidb)       (revision 5420)
2647
2648@@ -33,8 +33,11 @@
2649
2650     context_extras = {}
2651     if settings.DEBUG and request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS:
2652         context_extras['debug'] = True
2653-        from django.db import connection
2654-        context_extras['sql_queries'] = connection.queries
2655+        from django.db import connections
2656+        queries = []
2657+        for conn in connections:
2658+            queries.extend(connections[conn].connection.queries)
2659+        context_extras['sql_queries'] = queries
2660     return context_extras
2661 
2662 def i18n(request):
2663=== django/contrib/contenttypes/generic.py
2664==================================================================
2665--- django/contrib/contenttypes/generic.py      (/mirror/django/trunk)  (revision 5420)
2666
2667+++ django/contrib/contenttypes/generic.py      (/local/django/mymultidb)       (revision 5420)
2668
2669@@ -4,7 +4,6 @@
2670
2671 
2672 from django import oldforms
2673 from django.core.exceptions import ObjectDoesNotExist
2674-from django.db import connection
2675 from django.db.models import signals
2676 from django.db.models.fields.related import RelatedField, Field, ManyToManyRel
2677 from django.db.models.loading import get_model
2678@@ -154,7 +153,7 @@
2679
2680     def get_internal_type(self):
2681         return "ManyToManyField"
2682 
2683-    def db_type(self):
2684+    def db_type(self, connection):
2685         # Since we're simulating a ManyToManyField, in effect, best return the
2686         # same db_type as well.
2687         return None
2688@@ -184,7 +183,7 @@
2689
2690         superclass = rel_model._default_manager.__class__
2691         RelatedManager = create_generic_related_manager(superclass)
2692 
2693-        qn = connection.ops.quote_name
2694+        qn = rel_model._default_manager.db.connection.ops.quote_name
2695 
2696         manager = RelatedManager(
2697             model = rel_model,
2698=== django/contrib/auth/backends.py
2699==================================================================
2700--- django/contrib/auth/backends.py     (/mirror/django/trunk)  (revision 5420)
2701
2702+++ django/contrib/auth/backends.py     (/local/django/mymultidb)       (revision 5420)
2703
2704@@ -1,4 +1,3 @@
2705
2706-from django.db import connection
2707 from django.contrib.auth.models import User
2708 
2709 try:
2710@@ -23,6 +22,7 @@
2711
2712     def get_group_permissions(self, user_obj):
2713         "Returns a list of permission strings that this user has through his/her groups."
2714         if not hasattr(user_obj, '_group_perm_cache'):
2715+            connection = User._default_manager.db.connection
2716             cursor = connection.cursor()
2717             # The SQL below works out to the following, after DB quoting:
2718             # cursor.execute("""
2719=== tests/modeltests/multiple_databases (new directory)
2720==================================================================
2721=== tests/modeltests/multiple_databases/__init__.py
2722==================================================================
2723--- tests/modeltests/multiple_databases/__init__.py     (/mirror/django/trunk)  (revision 5420)
2724
2725+++ tests/modeltests/multiple_databases/__init__.py     (/local/django/mymultidb)       (revision 5420)
2726
2727@@ -0,0 +1 @@
2728
2729+pass
2730
2731=== tests/modeltests/multiple_databases/models.py
2732==================================================================
2733--- tests/modeltests/multiple_databases/models.py       (/mirror/django/trunk)  (revision 5420)
2734
2735+++ tests/modeltests/multiple_databases/models.py       (/local/django/mymultidb)       (revision 5420)
2736
2737@@ -0,0 +1,221 @@
2738
2739+"""
2740+XXX. Using multiple database connections
2741+
2742+Django normally uses only a single database connection. However,
2743+support is available for using any number of different, named
2744+connections. Multiple database support is entirely optional and has
2745+no impact on your application if you don't use it.
2746+
2747+Named connections are defined in your settings module. Create a
2748+`OTHER_DATABASES` variable that is a dict, mapping connection names to their
2749+particulars. The particulars are defined in a dict with the same keys
2750+as the variable names as are used to define the default connection, with one
2751+addition: MODELS.
2752+
2753+The MODELS item in an OTHER_DATABASES entry is a list of the apps and models
2754+that will use that connection.
2755+
2756+Access to named connections is through `django.db.connections`, which
2757+behaves like a dict: you access connections by name. Connections are
2758+established lazily, when accessed.  `django.db.connections[database]`
2759+holds a `ConnectionInfo` instance, with the attributes:
2760+`DatabaseError`, `backend`, `get_introspection_module`,
2761+`get_creation_module`, and `runshell`.
2762+
2763+To access a model's connection, use its manager. The connection is available
2764+at `model._default_manager.db.connection`. To find the backend or other
2765+connection metadata, use `model._meta.db` to access the full ConnectionInfo
2766+with connection metadata.
2767+"""
2768+
2769+from django.db import models
2770+
2771+class Artist(models.Model):
2772+    name = models.CharField(max_length=100)
2773+    alive = models.BooleanField(default=True)
2774+   
2775+    def __str__(self):
2776+        return self.name
2777+
2778+   
2779+class Opus(models.Model):
2780+    artist = models.ForeignKey(Artist)
2781+    name = models.CharField(max_length=100)
2782+    year = models.IntegerField()
2783+   
2784+    def __str__(self):
2785+        return "%s (%s)" % (self.name, self.year)
2786+
2787+
2788+class Widget(models.Model):
2789+    code = models.CharField(max_length=10, unique=True)
2790+    weight = models.IntegerField()
2791+
2792+    def __str__(self):
2793+        return self.code
2794+
2795+
2796+class DooHickey(models.Model):
2797+    name = models.CharField(max_length=50)
2798+    widgets = models.ManyToManyField(Widget, related_name='doohickeys')
2799+   
2800+    def __str__(self):
2801+        return self.name
2802+
2803+
2804+class Vehicle(models.Model):
2805+    make = models.CharField(max_length=20)
2806+    model = models.CharField(max_length=20)
2807+    year = models.IntegerField()
2808+
2809+    def __str__(self):
2810+        return "%d %s %s" % (self.year, self.make, self.model)
2811+
2812+
2813+__test__ = {'API_TESTS': """
2814+
2815+# See what connections are defined. django.db.connections acts like a dict.
2816+>>> from django.db import connection, connections, _default, model_connection_name
2817+>>> from django.conf import settings
2818+
2819+# Connections are referenced by name
2820+>>> connections['_a']
2821+Connection: ...
2822+>>> connections['_b']
2823+Connection: ...
2824+
2825+# Let's see what connections are available. The default connection is always
2826+# included in connections as well, and may be accessed as connections[_default].
2827+
2828+>>> connection_names = connections.keys()
2829+>>> connection_names.sort()
2830+>>> connection_names
2831+[<default>, '_a', '_b']
2832+   
2833+# Invalid connection names raise ImproperlyConfigured
2834+
2835+>>> connections['bad']
2836+Traceback (most recent call last):
2837+ ...
2838+ImproperlyConfigured: No database connection 'bad' has been configured
2839+
2840+# The model_connection_name() function will tell you the name of the
2841+# connection that a model is configured to use.
2842+
2843+>>> model_connection_name(Artist)
2844+'_a'
2845+>>> model_connection_name(Widget)
2846+'_b'
2847+>>> model_connection_name(Vehicle) is _default
2848+True
2849+>>> a = Artist(name="Paul Klee", alive=False)
2850+>>> a.save()
2851+>>> w = Widget(code='100x2r', weight=1000)
2852+>>> w.save()
2853+>>> v = Vehicle(make='Chevy', model='Camaro', year='1966')
2854+>>> v.save()
2855+>>> artists = Artist.objects.all()
2856+>>> list(artists)
2857+[<Artist: Paul Klee>]
2858+
2859+# Models can access their connections through the db property of their
2860+# default manager.
2861+
2862+>>> paul = _[0]
2863+>>> Artist.objects.db
2864+Connection: ... (ENGINE=... NAME=...)
2865+>>> paul._default_manager.db
2866+Connection: ... (ENGINE=... NAME=...)
2867+
2868+# When transactions are not managed, model save will commit only
2869+# for the model's connection.
2870+
2871+>>> from django.db import transaction
2872+>>> transaction.enter_transaction_management()
2873+>>> transaction.managed(False)
2874+>>> a = Artist(name="Joan Miro", alive=False)
2875+>>> w = Widget(code="99rbln", weight=1)
2876+>>> a.save()
2877+
2878+# Only connection '_a' is committed, so if we rollback
2879+# all connections we'll forget the new Widget.
2880+
2881+>>> transaction.rollback()
2882+>>> list(Artist.objects.all())
2883+[<Artist: Paul Klee>, <Artist: Joan Miro>]
2884+>>> list(Widget.objects.all())
2885+[<Widget: 100x2r>]
2886+
2887+# Managed transaction state applies across all connections.
2888+
2889+>>> transaction.managed(True)
2890+
2891+# When managed, just as when using a single connection, updates are
2892+# not committed until a commit is issued.
2893+
2894+>>> a = Artist(name="Pablo Picasso", alive=False)
2895+>>> a.save()
2896+>>> w = Widget(code="99rbln", weight=1)
2897+>>> w.save()
2898+>>> v = Vehicle(make='Pontiac', model='Fiero', year='1987')
2899+>>> v.save()
2900+
2901+# The connections argument may be passed to commit, rollback, and the
2902+# commit_on_success decorator as a keyword argument, as the first (for
2903+# commit and rollback) or second (for the decorator) positional
2904+# argument. It may be passed as a ConnectionInfo object, a connection
2905+# (DatabaseWrapper) object, a connection name, or a list or dict of
2906+# ConnectionInfo objects, connection objects, or connection names. If a
2907+# dict is passed, the keys are ignored and the values used as the list
2908+# of connections to commit, rollback, etc.
2909+
2910+>>> transaction.commit(connections['_b'])
2911+>>> transaction.commit('_b')
2912+>>> transaction.commit(connections='_b')
2913+>>> transaction.commit(connections=['_b'])
2914+>>> transaction.commit(['_a', '_b'])
2915+>>> transaction.commit(connections)
2916+
2917+# When the connections argument is omitted entirely, the transaction
2918+# command applies to all connections. Here we have committed
2919+# connections 'django_test_db_a' and 'django_test_db_b', but not the
2920+# default connection, so the new vehicle is lost on rollback.
2921+
2922+>>> transaction.rollback()
2923+>>> list(Artist.objects.all())
2924+[<Artist: Paul Klee>, <Artist: Joan Miro>, <Artist: Pablo Picasso>]
2925+>>> list(Widget.objects.all())
2926+[<Widget: 100x2r>, <Widget: 99rbln>]
2927+>>> list(Vehicle.objects.all())
2928+[<Vehicle: 1966 Chevy Camaro>]
2929+>>> transaction.rollback()
2930+>>> transaction.managed(False)
2931+>>> transaction.leave_transaction_management()
2932+
2933+# Of course, relations and all other normal database operations work
2934+# with models that use named connections just the same as with models
2935+# that use the default connection. The only caveat is that you can't
2936+# use a relation between two models that are stored in different
2937+# databases. Note that that doesn't mean that two models using
2938+# different connection *names* can't be related; only that in the the
2939+# context in which they are used, if you use the relation, the
2940+# connections named by the two models must resolve to the same
2941+# database.
2942+
2943+>>> a = Artist.objects.get(name="Paul Klee")
2944+>>> list(a.opus_set.all())
2945+[]
2946+>>> a.opus_set.create(name="Magic Garden", year="1926")
2947+<Opus: Magic Garden (1926)>
2948+>>> list(a.opus_set.all())
2949+[<Opus: Magic Garden (1926)>]
2950+>>> d = DooHickey(name='Thing')
2951+>>> d.save()
2952+>>> d.widgets.create(code='d101', weight=92)
2953+<Widget: d101>
2954+>>> list(d.widgets.all())
2955+[<Widget: d101>]
2956+>>> w = Widget.objects.get(code='d101')
2957+>>> list(w.doohickeys.all())
2958+[<DooHickey: Thing>]
2959+"""}
2960
2961Property changes on: tests/modeltests/multiple_databases
2962___________________________________________________________________
2963Name: svn:ignore
2964 +*.pyc
2965 +
2966
2967=== tests/regressiontests/manager_db    (new directory)
2968==================================================================
2969=== tests/regressiontests/manager_db/__init__.py
2970==================================================================
2971=== tests/regressiontests/manager_db/tests.py
2972==================================================================
2973--- tests/regressiontests/manager_db/tests.py   (/mirror/django/trunk)  (revision 5420)
2974
2975+++ tests/regressiontests/manager_db/tests.py   (/local/django/mymultidb)       (revision 5420)
2976
2977@@ -0,0 +1,17 @@
2978
2979+import unittest
2980+from regressiontests.manager_db.models import Insect
2981+
2982+class TestManagerDBAccess(unittest.TestCase):
2983+
2984+    def test_db_property(self):
2985+        m = Insect.objects
2986+        db = Insect.objects.db
2987+        assert db
2988+        assert db.connection
2989+        assert db.connection.cursor
2990+        assert db.backend
2991+        assert db.connection.ops.quote_name
2992+        assert db.get_creation_module
2993+
2994+if __name__ == '__main__':
2995+    unittest.main()
2996=== tests/regressiontests/manager_db/models.py
2997==================================================================
2998--- tests/regressiontests/manager_db/models.py  (/mirror/django/trunk)  (revision 5420)
2999
3000+++ tests/regressiontests/manager_db/models.py  (/local/django/mymultidb)       (revision 5420)
3001
3002@@ -0,0 +1,5 @@
3003
3004+from django.db import models
3005+
3006+class Insect(models.Model):
3007+    common_name = models.CharField(max_length=64)
3008+    latin_name = models.CharField(max_length=128)
3009
3010Property changes on: tests/regressiontests/manager_db
3011___________________________________________________________________
3012Name: svn:ignore
3013 +*.pyc
3014 +
3015
3016=== tests/regressiontests/thread_isolation      (new directory)
3017==================================================================
3018=== tests/regressiontests/thread_isolation/__init__.py
3019==================================================================
3020=== tests/regressiontests/thread_isolation/tests.py
3021==================================================================
3022--- tests/regressiontests/thread_isolation/tests.py     (/mirror/django/trunk)  (revision 5420)
3023
3024+++ tests/regressiontests/thread_isolation/tests.py     (/local/django/mymultidb)       (revision 5420)
3025
3026@@ -0,0 +1,251 @@
3027
3028+# tests that db settings can be different in different threads
3029+#
3030+#
3031+#    What's going on here:
3032+#
3033+#    Simulating multiple web requests in a threaded environment, one in
3034+#    which settings are different for each request. So we replace
3035+#    django.conf.settings with a thread local, with different
3036+#    configurations in each thread, and then fire off three
3037+#    simultaneous requests (using a condition to sync them up), and
3038+#    test that each thread sees its own settings and the models in each
3039+#    thread attempt to connect to the correct database as per their
3040+#    settings.
3041+#
3042+
3043+
3044+import copy
3045+import os
3046+import sys
3047+import threading
3048+import unittest
3049+from thread import get_ident
3050+
3051+from django.conf import settings, UserSettingsHolder
3052+from django.core.handlers.wsgi import WSGIHandler
3053+from django.db import model_connection_name, _default, connection, connections
3054+from regressiontests.request_isolation.tests import MockHandler
3055+from regressiontests.thread_isolation.models import *
3056+
3057+try:
3058+    # Only exists in Python 2.4+
3059+    from threading import local
3060+except ImportError:
3061+    # Import copy of _thread_local.py from Python 2.4
3062+    from django.utils._threading_local import local
3063+
3064+# helpers
3065+EV = threading.Event()
3066+
3067+class LocalSettings:
3068+    """Settings holder that allows thread-local overrides of defaults.
3069+    """
3070+    def __init__(self, defaults):
3071+        self._defaults = defaults
3072+        self._local = local()
3073+
3074+    def __getattr__(self, attr):
3075+        if attr in ('_defaults', '_local'):
3076+            return self.__dict__[attr]
3077+        _local = self.__dict__['_local']
3078+        _defaults = self.__dict__['_defaults']
3079+        debug("LS get %s (%s)", attr, hasattr(_local, attr))
3080+        if not hasattr(_local, attr):
3081+            # Make sure everything we return is the local version; this
3082+            # avoids sets to deep datastructures overwriting the defaults
3083+            setattr(_local, attr, copy.deepcopy(getattr(_defaults, attr)))
3084+        return getattr(_local, attr)
3085+
3086+    def __setattr__(self, attr, val):
3087+        if attr in ('_defaults', '_local'):
3088+            self.__dict__[attr] = val
3089+        else:
3090+            debug("LS set local %s = %s", attr, val)
3091+            setattr(self.__dict__['_local'], attr, val)
3092+
3093+def thread_two(func, *arg):
3094+    def start():
3095+        # from django.conf import settings
3096+        settings.OTHER_DATABASES['_b']['MODELS'] = []
3097+
3098+        debug("t2 ODB: %s", settings.OTHER_DATABASES)
3099+        debug("t2 waiting")
3100+        EV.wait(2.0)
3101+        func(*arg)
3102+        debug("t2 complete")
3103+    t2 = threading.Thread(target=start)
3104+    t2.start()
3105+    return t2
3106+
3107+def thread_three(func, *arg):
3108+    def start():
3109+        # from django.conf import settings           
3110+        settings.OTHER_DATABASES['_b']['MODELS'] = ['ti.MY']
3111+        settings.OTHER_DATABASES['_b'], \
3112+            settings.OTHER_DATABASES['_a'] = \
3113+            settings.OTHER_DATABASES['_a'], \
3114+            settings.OTHER_DATABASES['_b']
3115+
3116+        settings.DATABASE_NAME = \
3117+            settings.OTHER_DATABASES['_a']['DATABASE_NAME']
3118+
3119+        debug("t3 ODB: %s", settings.OTHER_DATABASES)
3120+        debug("3 %s: start: default: %s", get_ident(), settings.DATABASE_NAME)
3121+        debug("3 %s: start: conn: %s", get_ident(),
3122+              connection.settings.DATABASE_NAME)
3123+       
3124+        debug("t3 waiting")
3125+        EV.wait(2.0)
3126+        func(*arg)
3127+        debug("t3 complete")
3128+    t3 = threading.Thread(target=start)
3129+    t3.start()
3130+    return t3
3131+
3132+def debug(*arg):
3133+    pass
3134+##    msg, arg = arg[0], arg[1:]
3135+##    print msg % arg
3136+
3137+def start_response(code, headers):
3138+    debug("start response: %s %s", code, headers)
3139+    pass
3140+   
3141+class TestThreadIsolation(unittest.TestCase):
3142+    # event used to synchronize threads so we can be sure they are running
3143+    # together
3144+    lock = threading.RLock()
3145+    errors = []
3146+   
3147+    def setUp(self):
3148+        debug("setup")
3149+        self.settings = settings._target
3150+        settings._target = UserSettingsHolder(copy.deepcopy(settings._target))
3151+        settings.OTHER_DATABASES['_a']['MODELS'] =  ['ti.MX']
3152+        settings.OTHER_DATABASES['_b']['MODELS'] = ['ti.MY']
3153+
3154+        # normal settings holders aren't thread-safe, so we need to substitute
3155+        # one that is (and so allows per-thread settings)
3156+        holder = settings._target
3157+        settings._target = LocalSettings(holder)
3158+
3159+    def teardown(self):
3160+        debug("teardown")
3161+        settings._target = self.settings
3162+
3163+    def add_thread_error(self, err):
3164+        self.lock.acquire()
3165+        try:
3166+            self.errors.append(err)
3167+        finally:
3168+            self.lock.release()
3169+
3170+    def thread_errors(self):
3171+        self.lock.acquire()
3172+        try:
3173+            return self.errors[:]
3174+        finally:
3175+            self.lock.release()
3176+           
3177+    def request_one(self, request):
3178+        """Start out with settings as originally configured"""
3179+        from django.conf import settings
3180+        debug("request_one: %s", settings.OTHER_DATABASES)
3181+
3182+        self.assertEqual(model_connection_name(MQ), _default)
3183+        self.assertEqual(model_connection_name(MX), '_a')
3184+        self.assertEqual(
3185+            MX._default_manager.db.connection.settings.DATABASE_NAME,
3186+            settings.OTHER_DATABASES['_a']['DATABASE_NAME'])
3187+        self.assertEqual(model_connection_name(MY), '_b')
3188+        self.assertEqual(
3189+            MY._default_manager.db.connection.settings.DATABASE_NAME,
3190+            settings.OTHER_DATABASES['_b']['DATABASE_NAME'])
3191+        self.assert_(MQ._default_manager.db.connection is
3192+                     connections[_default].connection)
3193+        self.assertEqual(
3194+            MQ._default_manager.db.connection.settings.DATABASE_NAME,
3195+            settings.DATABASE_NAME)
3196+        self.assertEqual(connection.settings.DATABASE_NAME,
3197+                         settings.DATABASE_NAME)
3198+
3199+    def request_two(self, request):
3200+        """Between the first and second requests, settings change to assign
3201+        model MY to a different connection
3202+        """
3203+        # from django.conf import settings
3204+        debug("request_two: %s", settings.OTHER_DATABASES)
3205+
3206+        try:
3207+            self.assertEqual(model_connection_name(MQ), _default)
3208+            self.assertEqual(model_connection_name(MX), '_a')
3209+            self.assertEqual(
3210+                MX._default_manager.db.connection.settings.DATABASE_NAME,
3211+                settings.OTHER_DATABASES['_a']['DATABASE_NAME'])
3212+            self.assertEqual(model_connection_name(MY), _default)
3213+            self.assertEqual(
3214+                MY._default_manager.db.connection.settings.DATABASE_NAME,
3215+                settings.DATABASE_NAME)
3216+            self.assert_(MQ._default_manager.db.connection is
3217+                         connections[_default].connection)
3218+            self.assertEqual(
3219+                MQ._default_manager.db.connection.settings.DATABASE_NAME,
3220+                settings.DATABASE_NAME)
3221+            self.assertEqual(connection.settings.DATABASE_NAME,
3222+                             settings.DATABASE_NAME)
3223+        except:
3224+            self.add_thread_error(sys.exc_info())
3225+
3226+    def request_three(self, request):
3227+        """Between the 2nd and 3rd requests, the settings at the names in
3228+        OTHER_DATABASES have changed.
3229+        """
3230+        # from django.conf import settings
3231+        debug("3 %s: %s", get_ident(), settings.OTHER_DATABASES)
3232+        debug("3 %s: default: %s", get_ident(), settings.DATABASE_NAME)
3233+        debug("3 %s: conn: %s", get_ident(),
3234+              connection.settings.DATABASE_NAME)
3235+        try:
3236+            self.assertEqual(model_connection_name(MQ), _default)
3237+            self.assertEqual(model_connection_name(MX), '_b')
3238+            self.assertEqual(
3239+                MX._default_manager.db.connection.settings.DATABASE_NAME,
3240+                settings.OTHER_DATABASES['_b']['DATABASE_NAME'])
3241+            self.assertEqual(model_connection_name(MY), '_a')
3242+            self.assertEqual(
3243+                MY._default_manager.db.connection.settings.DATABASE_NAME,
3244+                settings.OTHER_DATABASES['_a']['DATABASE_NAME'])
3245+            self.assert_(MQ._default_manager.db.connection is
3246+                         connections[_default].connection)
3247+            self.assertEqual(
3248+                connection.settings.DATABASE_NAME,
3249+                settings.OTHER_DATABASES['_a']['DATABASE_NAME'])
3250+        except:
3251+            self.add_thread_error(sys.exc_info())
3252+       
3253+    def test_thread_isolation(self):
3254+       
3255+        debug("running tests")
3256+
3257+        env = os.environ.copy()
3258+        env['PATH_INFO'] = '/'
3259+        env['REQUEST_METHOD'] = 'GET'
3260+
3261+        t2 = thread_two(MockHandler(self.request_two), env, start_response)
3262+        t3 = thread_three(MockHandler(self.request_three), env, start_response)
3263+
3264+        try:
3265+            EV.set()
3266+            MockHandler(self.request_one)(env, start_response)
3267+        finally:
3268+            t2.join()
3269+            t3.join()
3270+            err = self.thread_errors()
3271+            if err:
3272+                import traceback
3273+                for e in err:
3274+                    traceback.print_exception(*e)
3275+                    raise AssertionError("%s thread%s failed" %
3276+                                         (len(err), len(err) > 1 and 's' or
3277+                                          ''))
3278+               
3279=== tests/regressiontests/thread_isolation/models.py
3280==================================================================
3281--- tests/regressiontests/thread_isolation/models.py    (/mirror/django/trunk)  (revision 5420)
3282
3283+++ tests/regressiontests/thread_isolation/models.py    (/local/django/mymultidb)       (revision 5420)
3284
3285@@ -0,0 +1,19 @@
3286
3287+from django.db import models
3288+
3289+# models
3290+class MQ(models.Model):
3291+    val = models.CharField(max_length=10)
3292+    class Meta:
3293+        app_label = 'ti'
3294+
3295+
3296+class MX(models.Model):
3297+    val = models.CharField(max_length=10)
3298+    class Meta:
3299+        app_label = 'ti'
3300+
3301+       
3302+class MY(models.Model):
3303+    val = models.CharField(max_length=10)
3304+    class Meta:
3305+        app_label = 'ti'
3306
3307Property changes on: tests/regressiontests/thread_isolation
3308___________________________________________________________________
3309Name: svn:ignore
3310 +*.pyc
3311 +
3312
3313=== tests/regressiontests/request_isolation     (new directory)
3314==================================================================
3315=== tests/regressiontests/request_isolation/__init__.py
3316==================================================================
3317=== tests/regressiontests/request_isolation/tests.py
3318==================================================================
3319--- tests/regressiontests/request_isolation/tests.py    (/mirror/django/trunk)  (revision 5420)
3320
3321+++ tests/regressiontests/request_isolation/tests.py    (/local/django/mymultidb)       (revision 5420)
3322
3323@@ -0,0 +1,100 @@
3324
3325+# tests that db settings can change between requests
3326+import copy
3327+import os
3328+import unittest
3329+from django.conf import settings, UserSettingsHolder
3330+from django.core.handlers.wsgi import WSGIHandler
3331+from django.db import models, model_connection_name, _default, connection
3332+from django.http import HttpResponse
3333+from regressiontests.request_isolation.models import *
3334+
3335+
3336+# helpers
3337+class MockHandler(WSGIHandler):
3338+
3339+    def __init__(self, test):
3340+        self.test = test
3341+        super(MockHandler, self).__init__()
3342+       
3343+    def get_response(self, request):
3344+        # debug("mock handler answering %s, %s", path, request)
3345+        return HttpResponse(self.test(request))
3346+
3347+
3348+def debug(*arg):
3349+    pass
3350+    # msg, arg = arg[0], arg[1:]
3351+    # print msg % arg
3352+
3353+
3354+def start_response(code, headers):
3355+    debug("start response: %s %s", code, headers)
3356+    pass
3357+
3358+# tests
3359+class TestRequestIsolation(unittest.TestCase):
3360+
3361+    def setUp(self):
3362+        debug("setup")
3363+        self.settings = settings._target
3364+        settings._target = UserSettingsHolder(copy.deepcopy(settings._target))
3365+        settings.OTHER_DATABASES['_a']['MODELS'] = ['ri.MX']
3366+        settings.OTHER_DATABASES['_b']['MODELS'] = ['ri.MY']
3367+
3368+    def tearDown(self):
3369+        debug("teardown")
3370+        settings._target = self.settings
3371+
3372+    def testRequestIsolation(self):
3373+        env = os.environ.copy()
3374+        env['PATH_INFO'] = '/'
3375+        env['REQUEST_METHOD'] = 'GET'
3376+
3377+        def request_one(request):
3378+            """Start out with settings as originally configured"""
3379+            self.assertEqual(model_connection_name(MX), '_a')
3380+            self.assertEqual(
3381+                MX._default_manager.db.connection.settings.DATABASE_NAME,
3382+                settings.OTHER_DATABASES['_a']['DATABASE_NAME'])
3383+            self.assertEqual(model_connection_name(MY), '_b')
3384+            self.assertEqual(
3385+                MY._default_manager.db.connection.settings.DATABASE_NAME,
3386+                settings.OTHER_DATABASES['_b']['DATABASE_NAME'])
3387+
3388+        def request_two(request):
3389+            """Between the first and second requests, settings change to assign
3390+            model MY to a different connection
3391+            """
3392+            self.assertEqual(model_connection_name(MX), '_a')
3393+            self.assertEqual(
3394+                MX._default_manager.db.connection.settings.DATABASE_NAME,
3395+                settings.OTHER_DATABASES['_a']['DATABASE_NAME'])
3396+            self.assertEqual(model_connection_name(MY), _default)
3397+            self.assertEqual(
3398+                MY._default_manager.db.connection.settings.DATABASE_NAME,
3399+                settings.DATABASE_NAME)
3400+
3401+        def request_three(request):
3402+            """Between the 2nd and 3rd requests, the settings at the names in
3403+            OTHER_DATABASES have changed.
3404+            """
3405+            self.assertEqual(model_connection_name(MX), '_b')
3406+            self.assertEqual(
3407+                MX._default_manager.db.connection.settings.DATABASE_NAME,
3408+                settings.OTHER_DATABASES['_b']['DATABASE_NAME'])
3409+            self.assertEqual(model_connection_name(MY), '_a')
3410+            self.assertEqual(
3411+                MY._default_manager.db.connection.settings.DATABASE_NAME,
3412+                settings.OTHER_DATABASES['_a']['DATABASE_NAME'])
3413+   
3414+        MockHandler(request_one)(env, start_response)
3415+
3416+        settings.OTHER_DATABASES['_b']['MODELS'] = []
3417+        MockHandler(request_two)(env, start_response)
3418+
3419+        settings.OTHER_DATABASES['_b']['MODELS'] = ['ri.MY']
3420+        settings.OTHER_DATABASES['_b'], \
3421+            settings.OTHER_DATABASES['_a'] = \
3422+            settings.OTHER_DATABASES['_a'], \
3423+            settings.OTHER_DATABASES['_b']
3424+        MockHandler(request_three)(env, start_response)
3425=== tests/regressiontests/request_isolation/models.py
3426==================================================================
3427--- tests/regressiontests/request_isolation/models.py   (/mirror/django/trunk)  (revision 5420)
3428
3429+++ tests/regressiontests/request_isolation/models.py   (/local/django/mymultidb)       (revision 5420)
3430
3431@@ -0,0 +1,13 @@
3432
3433+from django.db import models
3434+
3435+# models
3436+class MX(models.Model):
3437+    val = models.CharField(max_length=10)
3438+    class Meta:
3439+        app_label = 'ri'
3440+
3441+       
3442+class MY(models.Model):
3443+    val = models.CharField(max_length=10)
3444+    class Meta:
3445+        app_label = 'ri'
3446
3447Property changes on: tests/regressiontests/request_isolation
3448___________________________________________________________________
3449Name: svn:ignore
3450 +*.pyc
3451 +
3452
3453=== tests/runtests.py
3454==================================================================
3455--- tests/runtests.py   (/mirror/django/trunk)  (revision 5420)
3456
3457+++ tests/runtests.py   (/local/django/mymultidb)       (revision 5420)
3458
3459@@ -12,6 +12,14 @@
3460
3461 
3462 
3463 CONTRIB_DIR_NAME = 'django.contrib'
3464+TEST_OTHER_DATABASES = {
3465+    '_a': { 'DATABASE_NAME': 'django_test_a.db',
3466+            'MODELS': [ 'multiple_databases.Artist',
3467+            'multiple_databases.Opus' ]},
3468+    '_b': { 'DATABASE_NAME': 'django_test_b.db',
3469+            'MODELS': [ 'multiple_databases.Widget',
3470+            'multiple_databases.DooHickey' ]}
3471+}
3472 MODEL_TESTS_DIR_NAME = 'modeltests'
3473 REGRESSION_TESTS_DIR_NAME = 'regressiontests'
3474 
3475@@ -99,6 +107,7 @@
3476
3477 
3478     # Redirect some settings for the duration of these tests.
3479     settings.INSTALLED_APPS = ALWAYS_INSTALLED_APPS
3480+    settings.TEST_OTHER_DATABASES = TEST_OTHER_DATABASES
3481     settings.ROOT_URLCONF = 'urls'
3482     settings.TEMPLATE_DIRS = (os.path.join(os.path.dirname(__file__), TEST_TEMPLATE_DIR),)
3483     settings.USE_I18N = True
3484=== docs/settings.txt
3485==================================================================
3486--- docs/settings.txt   (/mirror/django/trunk)  (revision 5420)
3487
3488+++ docs/settings.txt   (/local/django/mymultidb)       (revision 5420)
3489
3490@@ -733,6 +733,13 @@
3491
3492 See `allowed date format strings`_. See also ``DATE_FORMAT``,
3493 ``DATETIME_FORMAT``, ``TIME_FORMAT`` and ``YEAR_MONTH_FORMAT``.
3494 
3495+OTHER_DATABASES
3496+---------------
3497+
3498+Default: ``{}``
3499+
3500+Other database connections to use in addition to the default connection. See the `multiple database support docs`_.
3501+
3502 PREPEND_WWW
3503 -----------
3504 
3505=== docs/multiple_database_support.txt
3506==================================================================
3507--- docs/multiple_database_support.txt  (/mirror/django/trunk)  (revision 5420)
3508
3509+++ docs/multiple_database_support.txt  (/local/django/mymultidb)       (revision 5420)
3510
3511@@ -0,0 +1,163 @@
3512
3513+========================
3514+Using Multiple Databases
3515+========================
3516+
3517+Standard Django practice is to use a single database connection for
3518+all models in all applications. However, Django supports configuring
3519+and using multiple database connections on a per-application, per-model
3520+or an ad-hoc basis. Using multiple database connections is optional.
3521+
3522+Configuring other database connections
3523+======================================
3524+
3525+Django's default database connection is configured via the settings
3526+DATABASE_ENGINE, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD,
3527+DATABASE_HOST, and DATABASE_PORT. Other connections are configured via
3528+the OTHER_DATABASES setting. Define OTHER_DATABASES as a dict, with a
3529+name for each connection as the key and a dict of settings as the
3530+value. In each OTHER_DATABASES entry (called a "named connection"),
3531+the keys are the same as the DATABASE_ENGINE, etc, settings used to
3532+configure the default connection. All keys are optional; any that are
3533+missing in a named connection's settings will inherit their values
3534+from the default connection.
3535+
3536+Here's an example::
3537+
3538+    DATABASE_ENGINE = 'postgresql'
3539+    DATABASE_NAME = 'django_apps'
3540+    DATABASE_USER = 'default_user'
3541+    DATABASE_PASSWORD = 'xxx'
3542+       
3543+    OTHER_DATABASES = {
3544+        'local': { 'DATABASE_ENGINE': 'sqlite3',
3545+                   'DATABASE_NAME': '/tmp/cache.db' },
3546+        'public': { 'DATABASE_HOST': 'public',
3547+                    'DATABASE_USER': 'public_user',
3548+                    'DATABASE_PASSWORD': 'xxx' }
3549+        'private': { 'DATABASE_HOST': 'private',
3550+                     'DATABASE_USER': 'private_user',
3551+                     'DATABASE_PASSWORD': 'xxx' }
3552+    }
3553+
3554+In addition to the DATABASE_* settings, each named connection in
3555+OTHER_DATABASES may optionally include a MODELS setting. This should
3556+be a list of app or app.model names, and is used to configure which
3557+models should use this connection instead of the default connection.
3558+
3559+Here's the example above, with ``MODELS``::
3560+
3561+    OTHER_DATABASES = {
3562+        'local': { 'DATABASE_ENGINE': 'sqlite3',
3563+                   'DATABASE_NAME': '/tmp/cache.db',
3564+                   # A model name: only the model ContentItem
3565+                   # with the app_label myapp will use this connection
3566+                   'MODELS': ['myapp.ContentItem'] },
3567+        'public': { 'DATABASE_HOST': 'public',
3568+                    'DATABASE_USER': 'public_user',
3569+                    'DATABASE_PASSWORD': 'xxx',
3570+                   # Two models in myapp will use the connection
3571+                    # named 'public', as will ALL models in
3572+                    # django.contribe.comments
3573+                   'MODELS': ['myapp.Blog','myapp.Article',
3574+                               'django.contrib.comments' ] }
3575+        # No models or apps are configured to use the private db
3576+        'private': { 'DATABASE_HOST': 'private',
3577+                     'DATABASE_USER': 'private_user',
3578+                     'DATABASE_PASSWORD': 'xxx' }
3579+    }
3580+
3581+Accessing a model's connection
3582+==============================
3583+
3584+Each manager has a ``db`` attribute that can be used to access the model's
3585+connection. Access the ``db`` attribute of a model's manager to obtain the
3586+model's currently configured connection.
3587+
3588+Example::
3589+
3590+    from django.db import models
3591+
3592+    class Blog(models.Model)
3593+        name = models.CharField(maxlength=50)
3594+
3595+    class Article(models.Model)
3596+       blog = models.ForeignKey(Blog)
3597+        title = models.CharField(maxlength=100)
3598+        slug = models.SlugField()
3599+        summary = models.CharField(maxlength=500)
3600+        body = models.TextField()
3601+
3602+    class ContentItem(models.Model)
3603+        slug = models.SlugField()
3604+        mimetype = models.CharField(maxlength=50)
3605+       file = models.FileField()
3606+       
3607+    # Get a ConnectionInfo instance that describes the connection
3608+    article_db = Article.objects.db
3609+   
3610+    # Get a connection and a cursor
3611+    connection = article_db.connection
3612+    cursor = connection.cursor()
3613+
3614+    # Get the ``quote_name`` function from the backend
3615+    qn = article_db.backend.quote_name
3616+
3617+Ordinarily you won't have to access a model's connection directly;
3618+just use the model and manager normally and they will use the
3619+connection configured for the model.
3620+
3621+ConnectionInfo objects
3622+======================
3623+
3624+FIXME Describe the ConnectionInfo object and each of its attributes.
3625+
3626+
3627+Accessing connections by name
3628+=============================
3629+
3630+Access named connections directly through
3631+``django.db.connections``. Each entry in ``django.db.connections`` is
3632+a ``ConnectionInfo`` instance bound to the settings configured in the
3633+OTHER_DATABASES entry under the same key.
3634+
3635+Example::
3636+
3637+    from django.db import connections
3638+
3639+    private_db = connections['private']
3640+    cursor = private_db.connection.cursor()
3641+
3642+
3643+Using transactions with other database connections
3644+==================================================
3645+
3646+Transaction managed state applies across all connections
3647+commit/rollback apply to all connections by default
3648+but you can specify individual connections or lists or dicts of connections
3649+
3650+
3651+Changing model connections on the fly
3652+=====================================
3653+
3654+Here's an example of primitive mirroring::
3655+
3656+    # Read all articles from the private db
3657+    # Note that we pull the articles into a list; this is necessary
3658+    # because query sets are lazy. If we were to change the model's
3659+    # connection without copying the articles into a local list, we'd
3660+    # wind up reading from public instead of private.
3661+
3662+    Article.objects.db = connections['private']
3663+    all_articles = list(Article.objects.all())
3664+   
3665+    # Save each article in the public db
3666+    Article.objects.db = connections['public']
3667+    for article in all_articles:
3668+        article.save()
3669+
3670+Thread and request isolation
3671+============================
3672+
3673+connections close after each request
3674+connection settings are thread-local
3675+