=== django/db/models/base.py
==================================================================
--- django/db/models/base.py	(/local/django)	(revision 5998)
+++ django/db/models/base.py	(/local/houdini/vendor/django)	(revision 5998)
@@ -7,7 +7,7 @@
 from django.db.models.related import RelatedObject
 from django.db.models.query import orderlist2sql, delete_objects
 from django.db.models.options import Options, AdminOptions
-from django.db import connection, backend, transaction
+from django.db import transaction
 from django.db.models import signals
 from django.db.models.loading import register_models
 from django.dispatch import dispatcher
@@ -38,7 +38,7 @@
                 new_class._meta.parents.extend(base._meta.parents)
 
         model_module = sys.modules[new_class.__module__]
-
+        
         if getattr(new_class._meta, 'app_label', None) is None:
             # Figure out the app_label by looking one level up.
             # For 'django.contrib.sites.models', this would be 'sites'.
@@ -154,6 +154,9 @@
     def save(self):
         dispatcher.send(signal=signals.pre_save, sender=self.__class__, instance=self)
 
+        info = self._meta.connection_info
+        connection = info.connection
+        backend = info.backend
         non_pks = [f for f in self._meta.fields if not f.primary_key]
         cursor = connection.cursor()
 
@@ -201,7 +204,7 @@
                      backend.get_pk_default_value()))
             if self._meta.has_auto_field and not pk_set:
                 setattr(self, self._meta.pk.attname, backend.get_last_insert_id(cursor, self._meta.db_table, self._meta.pk.column))
-        transaction.commit_unless_managed()
+        transaction.commit_unless_managed([connection])
 
         # Run any post-save hooks.
         dispatcher.send(signal=signals.post_save, sender=self.__class__, instance=self)
@@ -272,6 +275,7 @@
         return dict(field.choices).get(value, value)
 
     def _get_next_or_previous_by_FIELD(self, field, is_next, **kwargs):
+        backend = self._meta.connection_info.backend
         op = is_next and '>' or '<'
         where = '(%s %s %%s OR (%s = %%s AND %s.%s %s %%s))' % \
             (backend.quote_name(field.column), op, backend.quote_name(field.column),
@@ -286,6 +290,7 @@
             raise self.DoesNotExist, "%s matching query does not exist." % self.__class__._meta.object_name
 
     def _get_next_or_previous_in_order(self, is_next):
+        backend = self._meta.connection_info.backend
         cachename = "__%s_order_cache" % is_next
         if not hasattr(self, cachename):
             op = is_next and '>' or '<'
@@ -374,6 +379,9 @@
         rel = rel_field.rel.to
         m2m_table = rel_field.m2m_db_table()
         this_id = self._get_pk_val()
+        info = self._meta.connection_info
+        connection = info.connection
+        backend = info.backend
         cursor = connection.cursor()
         cursor.execute("DELETE FROM %s WHERE %s = %%s" % \
             (backend.quote_name(m2m_table),
@@ -383,7 +391,7 @@
             backend.quote_name(rel_field.m2m_column_name()),
             backend.quote_name(rel_field.m2m_reverse_name()))
         cursor.executemany(sql, [(this_id, i) for i in id_list])
-        transaction.commit_unless_managed()
+        transaction.commit_unless_managed([connection])
 
 ############################################
 # HELPER FUNCTIONS (CURRIED MODEL METHODS) #
@@ -392,6 +400,10 @@
 # ORDERING METHODS #########################
 
 def method_set_order(ordered_obj, self, id_list):
+    connection_info = ordered_obj.connection_info
+    connection = info.connection
+    backend = info.backend
+    
     cursor = connection.cursor()
     # Example: "UPDATE poll_choices SET _order = %s WHERE poll_id = %s AND id = %s"
     sql = "UPDATE %s SET %s = %%s WHERE %s = %%s AND %s = %%s" % \
@@ -400,9 +412,12 @@
         backend.quote_name(ordered_obj.pk.column))
     rel_val = getattr(self, ordered_obj.order_with_respect_to.rel.field_name)
     cursor.executemany(sql, [(i, rel_val, j) for i, j in enumerate(id_list)])
-    transaction.commit_unless_managed()
+    transaction.commit_unless_managed([connection])
 
 def method_get_order(ordered_obj, self):
+    connection_info = ordered_obj.connection_info
+    connection = info.connection
+    backend = info.backend
     cursor = connection.cursor()
     # Example: "SELECT id FROM poll_choices WHERE poll_id = %s ORDER BY _order"
     sql = "SELECT %s FROM %s WHERE %s = %%s ORDER BY %s" % \
=== django/db/models/manager.py
==================================================================
--- django/db/models/manager.py	(/local/django)	(revision 5998)
+++ django/db/models/manager.py	(/local/houdini/vendor/django)	(revision 5998)
@@ -99,6 +99,50 @@
     def values(self, *args, **kwargs):
         return self.get_query_set().values(*args, **kwargs)
 
+    #######################
+    # SCHEMA MANIPULATION #
+    #######################
+
+    def install(self, initial_data=False):
+        """Install my model's table, indexes and (if requested) initial data.
+
+        Returns a 2-tuple of the lists of statements executed and
+        statements pending. Pending statements are those that could
+        not yet be executed, such as foreign key constraints for
+        tables that don't exist at install time.
+        """
+        creator = self.model._meta.connection_info.get_creation_module()
+        run, pending = creator.get_create_table(self.model)
+        run += creator.get_create_indexes(self.model)
+        pending += creator.get_create_many_to_many(self.model)
+        if initial_data:
+            run += creator.get_initialdata(self.model)
+
+        executed = []
+        for statement in run:
+            statement.execute()
+            executed.append(statement)
+        return (executed, pending)
+
+    def load_initial_data(self):
+        """Load initial data for my model into the database."""
+        pass
+
+    def drop(self):
+        """Drop my model's table."""
+        pass
+
+    # Future...
+    
+    def add_column(self, column):
+        """Add a column to my model's table"""
+        pass
+
+    def drop_column(self, column):
+        """Drop a column from my model's table"""
+        pass
+        
+
 class ManagerDescriptor(object):
     # This class ensures managers aren't accessible via model instances.
     # For example, Poll.objects works, but poll_obj.objects raises AttributeError.
=== django/db/models/options.py
==================================================================
--- django/db/models/options.py	(/local/django)	(revision 5998)
+++ django/db/models/options.py	(/local/houdini/vendor/django)	(revision 5998)
@@ -1,4 +1,5 @@
 from django.conf import settings
+from django.db import connection_info, connections
 from django.db.models.related import RelatedObject
 from django.db.models.fields.related import ManyToManyRel
 from django.db.models.fields import AutoField, FieldDoesNotExist
@@ -11,7 +12,7 @@
 # Calculate the verbose_name by converting from InitialCaps to "lowercase with spaces".
 get_verbose_name = lambda class_name: re.sub('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))', ' \\1', class_name).lower().strip()
 
-DEFAULT_NAMES = ('verbose_name', 'db_table', 'ordering',
+DEFAULT_NAMES = ('verbose_name', 'db_connection', 'db_table', 'ordering',
                  'unique_together', 'permissions', 'get_latest_by',
                  'order_with_respect_to', 'app_label')
 
@@ -20,6 +21,7 @@
         self.fields, self.many_to_many = [], []
         self.module_name, self.verbose_name = None, None
         self.verbose_name_plural = None
+        self.db_connection = None
         self.db_table = ''
         self.ordering = []
         self.unique_together =  []
@@ -56,6 +58,7 @@
                 raise TypeError, "'class Meta' got invalid attribute(s): %s" % ','.join(meta_attrs.keys())
         else:
             self.verbose_name_plural = self.verbose_name + 's'
+        # 
         del self.meta
 
     def _prepare(self, model):
@@ -88,6 +91,17 @@
     def __repr__(self):
         return '<Options for %s>' % self.object_name
 
+    def get_connection_info(self):
+        if self.db_connection:
+            return connections[self.db_connection]
+        return connection_info
+    connection_info = property(get_connection_info)
+            
+    def get_connection(self):
+        """Get the database connection for this object's model"""
+        return self.get_connection_info().connection
+    connection = property(get_connection)
+    
     def get_field(self, name, many_to_many=True):
         "Returns the requested field by name. Raises FieldDoesNotExist on error."
         to_search = many_to_many and (self.fields + self.many_to_many) or self.fields
=== django/db/models/loading.py
==================================================================
--- django/db/models/loading.py	(/local/django)	(revision 5998)
+++ django/db/models/loading.py	(/local/houdini/vendor/django)	(revision 5998)
@@ -8,7 +8,9 @@
 _app_list = None # Cache of installed apps.
 _app_models = {} # Dictionary of models against app label
                  # Each value is a dictionary of model name: model class
-
+_app_model_order = {} # Dictionary of models against app label
+                      # Each value is a list of model names, in the order in
+                      # which the models were created
 def get_apps():
     "Returns a list of all installed modules that contain models."
     global _app_list
@@ -35,14 +37,21 @@
             return __import__(app_name, '', '', ['models']).models
     raise ImproperlyConfigured, "App with label %s could not be found" % app_label
 
-def get_models(app_mod=None):
+def get_models(app_mod=None, creation_order=False):
     """
     Given a module containing models, returns a list of the models. Otherwise
-    returns a list of all installed models.
+    returns a list of all installed models. In either case, if creation_order
+    is true, return the models sorted into the same order in which they
+    were created.
     """
     app_list = get_apps() # Run get_apps() to populate the _app_list cache. Slightly hackish.
     if app_mod:
-        return _app_models.get(app_mod.__name__.split('.')[-2], {}).values()
+        app_label = app_mod.__name__.split('.')[-2]
+        app_models = _app_models.get(app_label, {})
+        if creation_order:
+            return [ app_models[name]
+                     for name in _app_model_order.get(app_label, []) ]
+        return app_models.values()
     else:
         model_list = []
         for app_mod in app_list:
@@ -75,3 +84,5 @@
         model_name = model._meta.object_name.lower()
         model_dict = _app_models.setdefault(app_label, {})
         model_dict[model_name] = model
+        model_list = _app_model_order.setdefault(app_label, [])
+        model_list.append(model_name)
=== django/db/models/query.py
==================================================================
--- django/db/models/query.py	(/local/django)	(revision 5998)
+++ django/db/models/query.py	(/local/houdini/vendor/django)	(revision 5998)
@@ -1,4 +1,4 @@
-from django.db import backend, connection, transaction
+from django.db import backend, connection, connections, transaction
 from django.db.models.fields import DateField, FieldDoesNotExist
 from django.db.models import signals
 from django.dispatch import dispatcher
@@ -158,7 +158,7 @@
         # undefined, so we convert it to a list of tuples.
         extra_select = self._select.items()
 
-        cursor = connection.cursor()
+        cursor = self.model._meta.connection.cursor()
         select, sql, params = self._get_sql_clause()
         cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
         fill_cache = self._select_related
@@ -178,6 +178,11 @@
 
     def count(self):
         "Performs a SELECT COUNT() and returns the number of records as an integer."
+
+        info = self.model._meta.connection_info
+        backend = info.backend
+        connection = info.connection
+        
         counter = self._clone()
         counter._order_by = ()
         counter._offset = None
@@ -505,6 +510,9 @@
             columns = [f.column for f in self.model._meta.fields]
             field_names = [f.attname for f in self.model._meta.fields]
 
+        info = self.model._meta.connection_info
+        backend = info.backend
+        connection = info.connection
         cursor = connection.cursor()
         select, sql, params = self._get_sql_clause()
         select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns]
@@ -524,6 +532,11 @@
 class DateQuerySet(QuerySet):
     def iterator(self):
         from django.db.backends.util import typecast_timestamp
+
+        info = self.model._meta.connection_info
+        backend = info.backend
+        connection = info.connection
+        
         self._order_by = () # Clear this because it'll mess things up otherwise.
         if self._field.null:
             self._where.append('%s.%s IS NOT NULL' % \
@@ -616,7 +629,9 @@
         where2 = ['(NOT (%s))' % " AND ".join(where)]
         return tables, joins, where2, params
 
-def get_where_clause(lookup_type, table_prefix, field_name, value):
+def get_where_clause(opts, lookup_type, table_prefix, field_name, value):
+    backend = opts.connection_info.backend
+    
     if table_prefix.endswith('.'):
         table_prefix = backend.quote_name(table_prefix[:-1])+'.'
     field_name = backend.quote_name(field_name)
@@ -745,6 +760,10 @@
     intermediate_table = None
     join_required = False
 
+    info = current_opts.connection_info
+    backend = info.backend
+    connection = info.connection
+
     name = path.pop(0)
     # Has the primary key been requested? If so, expand it out
     # to be the name of the current class' primary key
@@ -872,7 +891,7 @@
         else:
             column = field.column
 
-        where.append(get_where_clause(clause, current_table + '.', column, value))
+        where.append(get_where_clause(current_opts, clause, current_table + '.', column, value))
         params.extend(field.get_db_prep_lookup(clause, value))
 
     return tables, joins, where, params
@@ -882,9 +901,14 @@
     ordered_classes = seen_objs.keys()
     ordered_classes.reverse()
 
-    cursor = connection.cursor()
 
     for cls in ordered_classes:
+
+        info = cls._meta.connection_info
+        backend = info.backend
+        connection = info.connection
+        cursor = connection.cursor()
+        
         seen_objs[cls] = seen_objs[cls].items()
         seen_objs[cls].sort()
 
@@ -919,6 +943,12 @@
 
     # Now delete the actual data
     for cls in ordered_classes:
+
+        info = cls._meta.connection_info
+        backend = info.backend
+        connection = info.connection
+        cursor = connection.cursor()
+
         seen_objs[cls].reverse()
         pk_list = [pk for pk,instance in seen_objs[cls]]
         for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
=== django/db/__init__.py
==================================================================
--- django/db/__init__.py	(/local/django)	(revision 5998)
+++ django/db/__init__.py	(/local/houdini/vendor/django)	(revision 5998)
@@ -1,5 +1,6 @@
-from django.conf import settings
+from django.conf import settings, UserSettingsHolder
 from django.core import signals
+from django.core.exceptions import ImproperlyConfigured
 from django.dispatch import dispatcher
 
 __all__ = ('backend', 'connection', 'DatabaseError')
@@ -7,36 +8,122 @@
 if not settings.DATABASE_ENGINE:
     settings.DATABASE_ENGINE = 'dummy'
 
-try:
-    backend = __import__('django.db.backends.%s.base' % settings.DATABASE_ENGINE, '', '', [''])
-except ImportError, e:
-    # The database backend wasn't found. Display a helpful error message
-    # listing all possible database backends.
-    from django.core.exceptions import ImproperlyConfigured
-    import os
-    backend_dir = os.path.join(__path__[0], 'backends')
-    available_backends = [f for f in os.listdir(backend_dir) if not f.startswith('_') and not f.startswith('.') and not f.endswith('.py') and not f.endswith('.pyc')]
-    available_backends.sort()
-    if settings.DATABASE_ENGINE not in available_backends:
-        raise ImproperlyConfigured, "%r isn't an available database backend. vailable options are: %s" % \
-            (settings.DATABASE_ENGINE, ", ".join(map(repr, available_backends)))
-    else:
-        raise # If there's some other error, this must be an error in Django itself.
+class ConnectionInfo(object):
+    """Encapsulates all information about a db connection:
+     - connection
+     - DatabaseError
+     - backend
+     - get_introspection_module
+     - get_creation_module
+     - runshell
+    """
+    def __init__(self, connection, DatabaseError, backend, 
+                 get_introspection_module, get_creation_module, runshell):
+        self.connection = connection
+        self.DatabaseError = DatabaseError
+        self.backend = backend
+        self.get_introspection_module = get_introspection_module
+        self.get_creation_module = get_creation_module
+        self.runshell = runshell
 
-get_introspection_module = lambda: __import__('django.db.backends.%s.introspection' % settings.DATABASE_ENGINE, '', '', [''])
-get_creation_module = lambda: __import__('django.db.backends.%s.creation' % settings.DATABASE_ENGINE, '', '', [''])
-runshell = lambda: __import__('django.db.backends.%s.client' % settings.DATABASE_ENGINE, '', '', ['']).runshell()
+    def __repr__(self):
+        return "Connection: %r (ENGINE=%s NAME=%s)" \
+               % (self.connection,
+                  self.connection.settings.DATABASE_ENGINE,
+                  self.connection.settings.DATABASE_NAME)
 
-connection = backend.DatabaseWrapper()
-DatabaseError = backend.DatabaseError
+def connect(settings=settings):
+    """Connect to the database specified in the given settings.
+    """
+    try:
+        backend = __import__('django.db.backends.%s.base' % settings.DATABASE_ENGINE, '', '', [''])
+    except ImportError, e:
+        # The database backend wasn't found. Display a helpful error message
+        # listing all possible database backends.
+        import os
+        backend_dir = os.path.join(__path__[0], 'backends')
+        available_backends = [f for f in os.listdir(backend_dir) if not f.startswith('_') and not f.startswith('.') and not f.endswith('.py') and not f.endswith('.pyc')]
+        available_backends.sort()
+        if settings.DATABASE_ENGINE not in available_backends:
+            raise ImproperlyConfigured, "%r isn't an available database backend. vailable options are: %s" % \
+                (settings.DATABASE_ENGINE, ", ".join(map(repr, available_backends)))
+        else:
+            raise # If there's some other error, this must be an error in Django itself.
 
-# Register an event that closes the database connection
-# when a Django request is finished.
-dispatcher.connect(connection.close, signal=signals.request_finished)
 
+    get_introspection_module = lambda: __import__('django.db.backends.%s.introspection' % settings.DATABASE_ENGINE, '', '', [''])
+    get_creation_module = lambda: __import__('django.db.backends.%s.creation' % settings.DATABASE_ENGINE, '', '', [''])
+    runshell = lambda: __import__('django.db.backends.%s.client' % settings.DATABASE_ENGINE, '', '', ['']).runshell()
+
+    connection = backend.DatabaseWrapper(settings)
+    DatabaseError = backend.DatabaseError
+
+    return ConnectionInfo(connection, DatabaseError, backend, 
+                          get_introspection_module, get_creation_module,
+                          runshell)
+
+connection_info = connect(settings)
+
+# backwards compatibility
+(connection, DatabaseError, backend, get_introspection_module, 
+ get_creation_module, runshell) = (connection_info.connection,
+                                   connection_info.DatabaseError,
+                                   connection_info.backend,
+                                   connection_info.get_introspection_module, 
+                                   connection_info.get_creation_module,
+                                   connection_info.runshell)
+
+
+class LazyConnectionManager(object):
+    """Manages named connections lazily, instantiating them as
+    they are requested.
+    """
+
+    def __init__(self):
+        self._connections = {}
+
+    def __iter__(self):
+        return self._connections.keys()
+
+    def __getattr__(self, attr):
+        return getattr(self._connections, attr)
+
+    def __getitem__(self, k):
+        try:
+            return self._connections[k]
+        except KeyError:
+            try:
+                self.connect(k)
+                return self._connections[k]
+            except KeyError:
+                raise ImproperlyConfigured, \
+                      "No database connection '%s' has been configured" % k
+
+    def __setattr(self, attr, val):
+        setattr(self._connections, attr, val)
+            
+    def connect(self, name):
+        from django.conf import settings
+        try:
+            database = settings.DATABASES[name]
+            try:
+                database.DATABASE_ENGINE
+            except AttributeError:
+                # assume its a dict and convert to settings instance
+                holder = UserSettingsHolder(settings)
+                for k, v in database.items():
+                    setattr(holder, k, v)
+                    database = holder
+                settings.DATABASES[name] = database
+            self._connections[name] = connect(database)
+        except AttributeError:
+            # no named connections to set up
+            pass
+connections = LazyConnectionManager()
+
 # Register an event that resets connection.queries
 # when a Django request is started.
-def reset_queries():
+def reset_queries(connection=connection):
     connection.queries = []
 dispatcher.connect(reset_queries, signal=signals.request_started)
 
@@ -46,3 +133,8 @@
     from django.db import transaction
     transaction.rollback_unless_managed()
 dispatcher.connect(_rollback_on_exception, signal=signals.got_request_exception)
+
+# Register an event that closes the database connection
+# when a Django request is finished.
+dispatcher.connect(connection.close, signal=signals.request_finished)
+
=== django/db/backends/ado_mssql/creation.py
==================================================================
--- django/db/backends/ado_mssql/creation.py	(/local/django)	(revision 5998)
+++ django/db/backends/ado_mssql/creation.py	(/local/houdini/vendor/django)	(revision 5998)
@@ -1,3 +1,5 @@
+from django.db.backends.ansi.creation import SchemaBuilder
+
 DATA_TYPES = {
     'AutoField':         'int IDENTITY (1, 1)',
     'BooleanField':      'bit',
@@ -24,3 +26,9 @@
     'URLField':          'varchar(200)',
     'USStateField':      'varchar(2)',
 }
+
+_builder = SchemaBuilder()
+get_create_table = _builder.get_create_table
+get_create_indexes = _builder.get_create_indexes
+get_create_many_to_many = _builder.get_create_many_to_many
+get_initialdata = _builder.get_initialdata
=== django/db/backends/postgresql/base.py
==================================================================
--- django/db/backends/postgresql/base.py	(/local/django)	(revision 5998)
+++ django/db/backends/postgresql/base.py	(/local/houdini/vendor/django)	(revision 5998)
@@ -21,12 +21,13 @@
     from django.utils._threading_local import local
 
 class DatabaseWrapper(local):
-    def __init__(self):
+    def __init__(self, settings):
+        self.settings = settings
         self.connection = None
         self.queries = []
 
     def cursor(self):
-        from django.conf import settings
+        settings = self.settings
         if self.connection is None:
             if settings.DATABASE_NAME == '':
                 from django.core.exceptions import ImproperlyConfigured
=== django/db/backends/postgresql/creation.py
==================================================================
--- django/db/backends/postgresql/creation.py	(/local/django)	(revision 5998)
+++ django/db/backends/postgresql/creation.py	(/local/houdini/vendor/django)	(revision 5998)
@@ -1,3 +1,5 @@
+from django.db.backends.ansi.creation import SchemaBuilder
+
 # This dictionary maps Field objects to their associated PostgreSQL column
 # types, as strings. Column-type strings can contain format strings; they'll
 # be interpolated against the values of Field.__dict__ before being output.
@@ -28,3 +30,9 @@
     'URLField':          'varchar(200)',
     'USStateField':      'varchar(2)',
 }
+
+_builder = SchemaBuilder()
+get_create_table = _builder.get_create_table
+get_create_indexes = _builder.get_create_indexes
+get_create_many_to_many = _builder.get_create_many_to_many
+get_initialdata = _builder.get_initialdata
=== django/db/backends/sqlite3/base.py
==================================================================
--- django/db/backends/sqlite3/base.py	(/local/django)	(revision 5998)
+++ django/db/backends/sqlite3/base.py	(/local/houdini/vendor/django)	(revision 5998)
@@ -34,12 +34,13 @@
     from django.utils._threading_local import local
 
 class DatabaseWrapper(local):
-    def __init__(self):
+    def __init__(self, settings):
+        self.settings = settings
         self.connection = None
         self.queries = []
 
     def cursor(self):
-        from django.conf import settings
+        settings = self.settings
         if self.connection is None:
             self.connection = Database.connect(settings.DATABASE_NAME,
                 detect_types=Database.PARSE_DECLTYPES | Database.PARSE_COLNAMES)
=== django/db/backends/sqlite3/creation.py
==================================================================
--- django/db/backends/sqlite3/creation.py	(/local/django)	(revision 5998)
+++ django/db/backends/sqlite3/creation.py	(/local/houdini/vendor/django)	(revision 5998)
@@ -1,3 +1,5 @@
+from django.db.backends.ansi.creation import SchemaBuilder
+
 # SQLite doesn't actually support most of these types, but it "does the right
 # thing" given more verbose field definitions, so leave them as is so that
 # schema inspection is more useful.
@@ -27,3 +29,9 @@
     'URLField':                     'varchar(200)',
     'USStateField':                 'varchar(2)',
 }
+
+_builder = SchemaBuilder()
+get_create_table = _builder.get_create_table
+get_create_indexes = _builder.get_create_indexes
+get_create_many_to_many = _builder.get_create_many_to_many
+get_initialdata = _builder.get_initialdata
=== django/db/backends/mysql/creation.py
==================================================================
--- django/db/backends/mysql/creation.py	(/local/django)	(revision 5998)
+++ django/db/backends/mysql/creation.py	(/local/houdini/vendor/django)	(revision 5998)
@@ -1,3 +1,5 @@
+from django.db.backends.ansi.creation import SchemaBuilder
+
 # This dictionary maps Field objects to their associated MySQL column
 # types, as strings. Column-type strings can contain format strings; they'll
 # be interpolated against the values of Field.__dict__ before being output.
@@ -28,3 +30,9 @@
     'URLField':          'varchar(200)',
     'USStateField':      'varchar(2)',
 }
+
+_builder = SchemaBuilder()
+get_create_table = _builder.get_create_table
+get_create_indexes = _builder.get_create_indexes
+get_create_many_to_many = _builder.get_create_many_to_many
+get_initialdata = _builder.get_initialdata
=== django/db/backends/oracle/creation.py
==================================================================
--- django/db/backends/oracle/creation.py	(/local/django)	(revision 5998)
+++ django/db/backends/oracle/creation.py	(/local/houdini/vendor/django)	(revision 5998)
@@ -1,3 +1,5 @@
+from django.db.backends.ansi.creation import SchemaBuilder
+
 DATA_TYPES = {
     'AutoField':         'number(38)',
     'BooleanField':      'number(1)',
@@ -24,3 +26,9 @@
     'URLField':          'varchar(200)',
     'USStateField':      'varchar(2)',
 }
+
+_builder = SchemaBuilder()
+get_create_table = _builder.get_create_table
+get_create_indexes = _builder.get_create_indexes
+get_create_many_to_many = _builder.get_create_many_to_many
+get_initialdata = _builder.get_initialdata
=== django/db/backends/ansi/__init__.py
==================================================================
--- django/db/backends/ansi/__init__.py	(/local/django)	(revision 5998)
+++ django/db/backends/ansi/__init__.py	(/local/houdini/vendor/django)	(revision 5998)
@@ -0,0 +1 @@
+pass
=== django/db/backends/ansi/creation.py
==================================================================
--- django/db/backends/ansi/creation.py	(/local/django)	(revision 5998)
+++ django/db/backends/ansi/creation.py	(/local/houdini/vendor/django)	(revision 5998)
@@ -0,0 +1,197 @@
+"""ANSISQL schema manipulation functions and classes
+"""
+from django.db import models
+
+# FIXME correct handling of styles,
+# allow style object to be passed in
+class dummy:
+    def __getattr__(self, attr):
+        return lambda x: x
+
+class BoundStatement(object):
+    """Represents an SQL statement that is to be executed, at some point in
+    the future, using a specific database connection.
+    """
+    def __init__(self, sql, connection):
+        self.sql = sql
+        self.connection = connection
+
+    def execute(self):
+        cursor = self.connection.cursor()
+        cursor.execute(self.sql)
+
+    def __repr__(self):
+        return "BoundStatement(%r)" % self.sql
+
+    def __str__(self):
+        return self.sql
+
+    def __eq__(self, other):
+        return self.sql == other.sql and self.connection == other.connection
+
+class SchemaBuilder(object):
+    """Basic ANSI SQL schema element builder. Instances of this class may be
+    used to construct SQL expressions that create or drop schema elements such
+    as tables, indexes and (for those backends that support them) foreign key
+    or other constraints.
+    """
+    def __init__(self):
+        self.models_already_seen = []
+        
+    def get_create_table(self, model, style=dummy()):
+        """Construct and return the SQL expression(s) needed to create the
+        table for the given model, and any constraints on that
+        table. The return value is a 2-tuple. The first element of the tuple
+        is a list of BoundStatements that may be executed immediately. The
+        second is a list of BoundStatements representing constraints that
+        can't be executed immediately because (for instance) the referent
+        table does not exist.
+        """
+        if model in self.models_already_seen:
+            return (None, None, None)
+        self.models_already_seen.append(model)
+        
+        opts = model._meta
+        info = opts.connection_info
+        backend = info.backend
+        quote_name = backend.quote_name
+        data_types = info.get_creation_module().DATA_TYPES
+        table_output = []
+        pending_references = {} 
+        pending = [] # actual pending statements to execute
+        for f in opts.fields:
+            if isinstance(f, models.ForeignKey):
+                rel_field = f.rel.get_related_field()
+                data_type = self.get_rel_data_type(rel_field)
+            else:
+                rel_field = f
+                data_type = f.get_internal_type()
+            col_type = data_types[data_type]
+            if col_type is not None:
+                # Make the definition (e.g. 'foo VARCHAR(30)') for this field.
+                field_output = [style.SQL_FIELD(quote_name(f.column)),
+                    style.SQL_COLTYPE(col_type % rel_field.__dict__)]
+                field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or '')))
+                if f.unique:
+                    field_output.append(style.SQL_KEYWORD('UNIQUE'))
+                if f.primary_key:
+                    field_output.append(style.SQL_KEYWORD('PRIMARY KEY'))
+                if f.rel:
+                    if f.rel.to in self.models_already_seen:
+                        field_output.append(style.SQL_KEYWORD('REFERENCES') + ' ' + \
+                            style.SQL_TABLE(quote_name(f.rel.to._meta.db_table)) + ' (' + \
+                            style.SQL_FIELD(quote_name(f.rel.to._meta.get_field(f.rel.field_name).column)) + ')'
+                        )
+                    else:
+                        # We haven't yet created the table to which this field
+                        # is related, so save it for later.
+                        pr = pending_references.setdefault(f.rel.to, []).append(f)
+                table_output.append(' '.join(field_output))
+        if opts.order_with_respect_to:
+            table_output.append(style.SQL_FIELD(quote_name('_order')) + ' ' + \
+                style.SQL_COLTYPE(data_types['IntegerField']) + ' ' + \
+                style.SQL_KEYWORD('NULL'))
+        for field_constraints in opts.unique_together:
+            table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \
+                ", ".join([quote_name(style.SQL_FIELD(opts.get_field(f).column)) for f in field_constraints]))
+
+        full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(quote_name(opts.db_table)) + ' (']
+        for i, line in enumerate(table_output): # Combine and add commas.
+            full_statement.append('    %s%s' % (line, i < len(table_output)-1 and ',' or ''))
+        full_statement.append(');')
+
+        create = [BoundStatement('\n'.join(full_statement), opts.connection)]
+
+        if (pending_references and
+            backend.supports_constraints):
+            for rel_class, cols in pending_references.items():
+                for f in cols:
+                    rel_opts = rel_class._meta
+                    r_table = rel_opts.db_table
+                    r_col = f.column
+                    table = opts.db_table
+                    col = opts.get_field(f.rel.field_name).column
+                    sql = style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s);' % \
+                        (quote_name(table),
+                        quote_name('%s_referencing_%s_%s' % (r_col, r_table, col)),
+                        quote_name(r_col), quote_name(r_table), quote_name(col))
+                    pending.append(BoundStatement(sql, opts.connection))
+        return (create, pending)    
+
+    def get_create_indexes(self, model, style=dummy()):
+        """Construct and return SQL statements needed to create the indexes for
+        a model. Returns a list of BoundStatements.
+        """
+        info = model._meta.connection_info
+        backend = info.backend
+        connection = info.connection
+        output = []
+        for f in model._meta.fields:
+            if f.db_index:
+                unique = f.unique and 'UNIQUE ' or ''
+                output.append(
+                    BoundStatement(
+                        ' '.join(
+                            [style.SQL_KEYWORD('CREATE %sINDEX' % unique), 
+                             style.SQL_TABLE('%s_%s' %
+                                             (model._meta.db_table, f.column)),
+                             style.SQL_KEYWORD('ON'), 
+                             style.SQL_TABLE(
+                                    backend.quote_name(model._meta.db_table)),
+                             "(%s);" % style.SQL_FIELD(
+                                    backend.quote_name(f.column))]),
+                        connection)
+                    )
+        return output
+
+    def get_create_many_to_many(self, model, style=dummy()):
+        """Construct and return SQL statements needed to create the
+        tables and relationships for all many-to-many relations
+        defined in the model. Returns a list of bound statments. Note
+        that these statements should only be execute after all models
+        for an app have been created.
+        """
+        info = model._meta.connection_info
+        backend = info.backend
+        connection = info.connection        
+        data_types = info.get_creation_module().DATA_TYPES
+        opts = model._meta
+        output = []        
+        for f in opts.many_to_many:
+            if not isinstance(f.rel, models.GenericRel):
+                table_output = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + \
+                    style.SQL_TABLE(backend.quote_name(f.m2m_db_table())) + ' (']
+                table_output.append('    %s %s %s,' % \
+                    (style.SQL_FIELD(backend.quote_name('id')),
+                    style.SQL_COLTYPE(data_types['AutoField']),
+                    style.SQL_KEYWORD('NOT NULL PRIMARY KEY')))
+                table_output.append('    %s %s %s %s (%s),' % \
+                    (style.SQL_FIELD(backend.quote_name(f.m2m_column_name())),
+                    style.SQL_COLTYPE(data_types[self.get_rel_data_type(opts.pk)] % opts.pk.__dict__),
+                    style.SQL_KEYWORD('NOT NULL REFERENCES'),
+                    style.SQL_TABLE(backend.quote_name(opts.db_table)),
+                    style.SQL_FIELD(backend.quote_name(opts.pk.column))))
+                table_output.append('    %s %s %s %s (%s),' % \
+                    (style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name())),
+                    style.SQL_COLTYPE(data_types[self.get_rel_data_type(f.rel.to._meta.pk)] % f.rel.to._meta.pk.__dict__),
+                    style.SQL_KEYWORD('NOT NULL REFERENCES'),
+                    style.SQL_TABLE(backend.quote_name(f.rel.to._meta.db_table)),
+                    style.SQL_FIELD(backend.quote_name(f.rel.to._meta.pk.column))))
+                table_output.append('    %s (%s, %s)' % \
+                    (style.SQL_KEYWORD('UNIQUE'),
+                    style.SQL_FIELD(backend.quote_name(f.m2m_column_name())),
+                    style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name()))))
+                table_output.append(');')
+                output.append(BoundStatement('\n'.join(table_output),
+                                             connection))
+        return output
+
+    
+    def get_initialdata(self, model, style=dummy()):
+        return [] # FIXME
+
+    def get_rel_data_type(self, f):
+        return (f.get_internal_type() in ('AutoField', 'PositiveIntegerField',
+                                          'PositiveSmallIntegerField')) \
+                                          and 'IntegerField' \
+                                          or f.get_internal_type()
=== django/db/transaction.py
==================================================================
--- django/db/transaction.py	(/local/django)	(revision 5998)
+++ django/db/transaction.py	(/local/houdini/vendor/django)	(revision 5998)
@@ -16,7 +16,7 @@
     import thread
 except ImportError:
     import dummy_thread as thread
-from django.db import connection
+from django import db
 from django.conf import settings
 
 class TransactionManagementError(Exception):
@@ -116,48 +116,67 @@
     Puts the transaction manager into a manual state: managed transactions have
     to be committed explicitly by the user. If you switch off transaction
     management and there is a pending commit/rollback, the data will be
-    commited.
+    commited. Note that managed state applies across all connections.
     """
     thread_ident = thread.get_ident()
     top = state.get(thread_ident, None)
     if top:
         top[-1] = flag
         if not flag and is_dirty():
-            connection._commit()
+            for cx in all_connections():
+                cx._commit()
             set_clean()
     else:
         raise TransactionManagementError("This code isn't under transaction management")
 
-def commit_unless_managed():
+def commit_unless_managed(connections=None):
     """
     Commits changes if the system is not in managed transaction mode.
     """
     if not is_managed():
-        connection._commit()
+        if connections is None:
+            connections = all_connections()
+        else:
+            connections = ensure_connections(connections)
+        for cx in connections:
+            cx._commit()
     else:
         set_dirty()
 
-def rollback_unless_managed():
+def rollback_unless_managed(connections=None):
     """
     Rolls back changes if the system is not in managed transaction mode.
     """
     if not is_managed():
-        connection._rollback()
+        if connections is None:
+            connections = all_connections()
+        for cx in connections:
+            cx._rollback()
     else:
         set_dirty()
 
-def commit():
+def commit(connections=None):
     """
     Does the commit itself and resets the dirty flag.
     """
-    connection._commit()
+    if connections is None:
+        connections = all_connections()
+    else:
+        connections = ensure_connections(connections)
+    for cx in connections:
+        cx._commit()
     set_clean()
 
-def rollback():
+def rollback(connections=None):
     """
     This function does the rollback itself and resets the dirty flag.
     """
-    connection._rollback()
+    if connections is None:
+        connections = all_connections()
+    else:
+        connections = ensure_connections(connections)
+    for cx in connections:
+        cx._rollback()
     set_clean()
 
 ##############
@@ -179,7 +198,7 @@
             leave_transaction_management()
     return _autocommit
 
-def commit_on_success(func):
+def commit_on_success(func, connections=None):
     """
     This decorator activates commit on response. This way, if the view function
     runs successfully, a commit is made; if the viewfunc produces an exception,
@@ -198,7 +217,7 @@
                 raise
             else:
                 if is_dirty():
-                    commit()
+                    commit(connections)
             return res
         finally:
             leave_transaction_management()
@@ -220,3 +239,29 @@
             leave_transaction_management()
 
     return _commit_manually
+
+###########
+# HELPERS #
+###########
+
+def all_connections():
+    return [db.connection] + [ c.connection
+                               for c in db.connections.values() ]
+
+def ensure_connections(val):
+    connections = []
+    if isinstance(val, basestring):
+        val = [val]
+    try:
+        iter(val)
+    except:
+        val = [val]
+    for cx in val:
+        if hasattr(cx, 'cursor'):
+            connections.append(cx)
+        elif hasattr(cx, 'connection'):
+            connections.append(cx.connection)
+        elif isinstance(cx, basestring):
+            connections.append(db.connections[cx].connection)
+    return connections
+        
=== tests/modeltests/multiple_databases/__init__.py
==================================================================
--- tests/modeltests/multiple_databases/__init__.py	(/local/django)	(revision 5998)
+++ tests/modeltests/multiple_databases/__init__.py	(/local/houdini/vendor/django)	(revision 5998)
@@ -0,0 +1 @@
+pass
=== tests/modeltests/multiple_databases/models.py
==================================================================
--- tests/modeltests/multiple_databases/models.py	(/local/django)	(revision 5998)
+++ tests/modeltests/multiple_databases/models.py	(/local/houdini/vendor/django)	(revision 5998)
@@ -0,0 +1,182 @@
+"""
+N. Using multiple database connections
+
+Django normally uses only a single database connection. However, support is
+available for connecting to any number of different, named databases on a
+per-model-class level.
+
+Please note that this test uses hard-coded databases, and will fail
+unless sqlite3 is install and /tmp is writeable. It also will
+currently fail unless the two temp databases are removed by hand
+between runs. It also side-effects the settings as set in the
+DJANGO_SETTINGS_MODULE. All of these are undesirable, and mean that
+this test is almost certainly going to have to move to othertests.
+
+It would be really nice if the testrunner supported setup and teardown.
+
+"""
+
+# models can define which connections they will use
+from django.db import models
+
+class Artist(models.Model):
+    name = models.CharField(maxlength=100)
+    alive = models.BooleanField(default=True)
+
+    def __str__(self):
+        return self.name
+
+    class Meta:
+        db_connection = 'a'
+        
+class Widget(models.Model):
+    code = models.CharField(maxlength=10, unique=True)
+    weight = models.IntegerField()
+
+    def __str__(self):
+        return self.code
+    
+    class Meta:
+        db_connection = 'b'
+
+
+# but they don't have to
+class Vehicle(models.Model):
+    make = models.CharField(maxlength=20)
+    model = models.CharField(maxlength=20)
+    year = models.IntegerField()
+
+    def __str__(self):
+        return "%d %s %s" % (self.year, self.make, self.model)
+
+API_TESTS = """
+
+# Connections are established lazily, when requested by name.
+
+>>> from django.conf import settings
+>>> settings.DATABASES = { 
+...     'a': { 'DATABASE_ENGINE': 'sqlite3',
+...            'DATABASE_NAME': '/tmp/dba.db'
+...     },
+...     'b': { 'DATABASE_ENGINE': 'sqlite3',
+...            'DATABASE_NAME': '/tmp/dbb.db'
+...     }}
+>>> from django.db import connections
+
+# connections[database] holds a tuple of connection, DatabaseError, backend, 
+# get_introspection_module, get_creation_module, and runshell
+
+>>> connections['a']
+Connection: <django.db.backends.sqlite3.base.DatabaseWrapper object at ...> (ENGINE=sqlite3 NAME=/tmp/dba.db)
+>>> connections['b']
+Connection: <django.db.backends.sqlite3.base.DatabaseWrapper object at ...> (ENGINE=sqlite3 NAME=/tmp/dbb.db)
+
+# Invalid connection names raise ImproperlyConfigured
+
+>>> connections['bad']
+Traceback (most recent call last):
+    ...
+ImproperlyConfigured: No database connection 'bad' has been configured
+
+# For the time being, we have to install the models by hand
+
+>>> from django.core import management
+>>> artist_sql = ''.join(management._get_sql_model_create(Artist)[0])
+>>> cursor = Artist._meta.connection.cursor()
+>>> cursor.execute(artist_sql)
+>>> widget_sql = ''.join(management._get_sql_model_create(Widget)[0])
+>>> cursor = Widget._meta.connection.cursor()
+>>> cursor.execute(widget_sql)
+
+# Then we can play with them
+
+>>> a = Artist(name="Paul Klee", alive=False)
+>>> a.save()
+>>> a._meta.connection.settings.DATABASE_NAME
+'/tmp/dba.db'
+>>> w = Widget(code='100x2r', weight=1000)
+>>> w.save()
+>>> w._meta.connection.settings.DATABASE_NAME
+'/tmp/dbb.db'
+>>> v = Vehicle(make='Chevy', model='Camaro', year='1966')
+>>> v.save()
+>>> v._meta.connection.settings.DATABASE_NAME
+':memory:'
+
+# Managers use their models' connections
+
+>>> artists = Artist.objects.all()
+>>> list(artists)
+[<Artist: Paul Klee>]
+>>> artists[0]._meta.connection.settings.DATABASE_NAME
+'/tmp/dba.db'
+>>> widgets = Widget.objects.all()
+>>> list(widgets)
+[<Widget: 100x2r>]
+>>> widgets[0]._meta.connection.settings.DATABASE_NAME
+'/tmp/dbb.db'
+>>> vehicles = Vehicle.objects.all()
+>>> list(vehicles)
+[<Vehicle: 1966 Chevy Camaro>]
+>>> vehicles[0]._meta.connection.settings.DATABASE_NAME
+':memory:'
+
+# When not using transaction management, model save will commit only
+# for the model's connection
+
+>>> from django.db import transaction
+>>> transaction.enter_transaction_management()
+>>> a = Artist(name="Joan Miro", alive=False)
+>>> w = Widget(code="99rbln", weight=1)
+>>> a.save()
+
+# only connection 'a' is committed, so if we rollback all connections
+# we'll forget the new Widget.
+
+>>> transaction.rollback()
+>>> list(Artist.objects.all())
+[<Artist: Paul Klee>, <Artist: Joan Miro>]
+>>> list(Widget.objects.all())
+[<Widget: 100x2r>]
+
+# Managed transaction state applies across all connections.
+
+>>> transaction.managed(True)
+
+# When managed, just as when using a single connection, updates are
+# not committed until a commit is issued.
+
+>>> a = Artist(name="Pablo Picasso", alive=False)
+>>> a.save()
+>>> w = Widget(code="99rbln", weight=1)
+>>> w.save()
+>>> v = Vehicle(make='Pontiac', model='Fiero', year='1987')
+>>> v.save()
+
+# The connections argument may be passed to commit, rollback, and the
+# commit_on_success decorator as a keyword argument, or the first (for commit
+# and rollback) or second (for the decorator) positional argument. It
+# may be passed as a single connection info object, a single
+# connection object, a single connection name, or a list of connection
+# info objects, connection objects, or connection names.
+
+>>> transaction.commit(connections['b'])
+>>> transaction.commit('b')
+>>> transaction.commit(['a', 'b'])
+>>> transaction.commit(connections)
+
+# When omitted entirely, the transaction command applies to all
+# connections. Here we have committed connections 'a' and 'b', but not
+# the default connection, so the new vehicle is lost on rollback.
+
+>>> transaction.rollback()
+>>> list(Artist.objects.all())
+[<Artist: Paul Klee>, <Artist: Joan Miro>, <Artist: Pablo Picasso>]
+>>> list(Widget.objects.all())
+[<Widget: 100x2r>, <Widget: 99rbln>]
+>>> list(Vehicle.objects.all())
+[<Vehicle: 1966 Chevy Camaro>]
+
+>>> transaction.leave_transaction_management()
+
+"""
=== tests/modeltests/manager_schema_manipulation/__init__.py
==================================================================
--- tests/modeltests/manager_schema_manipulation/__init__.py	(/local/django)	(revision 5998)
+++ tests/modeltests/manager_schema_manipulation/__init__.py	(/local/houdini/vendor/django)	(revision 5998)
@@ -0,0 +1 @@
+pass
=== tests/modeltests/manager_schema_manipulation/models.py
==================================================================
--- tests/modeltests/manager_schema_manipulation/models.py	(/local/django)	(revision 5998)
+++ tests/modeltests/manager_schema_manipulation/models.py	(/local/houdini/vendor/django)	(revision 5998)
@@ -0,0 +1,132 @@
+"""
+N. Schema manipulations
+
+Django uses a model's manager to perform schema manipulations such as
+creating or dropping the model's table.
+
+Please note that your settings file must define DATABASES with names
+'p' and 'q' for this test.
+"""
+
+from django.db import models
+
+# default connection
+class DA(models.Model):
+    name = models.CharField(maxlength=20)
+
+    def __str__(self):
+        return self.name
+    
+# connection p
+class PA(models.Model):
+    name = models.CharField(maxlength=20)
+    # This creates a cycle in the dependency graph
+    c = models.ForeignKey('PC', null=True)
+    
+    def __str__(self):
+        return self.name
+
+    class Meta:
+        db_connection = 'p'
+
+class PB(models.Model):
+    name = models.CharField(maxlength=20)
+    a = models.ForeignKey(PA)
+
+    def __str__(self):
+        return self.name
+
+    class Meta:
+        db_connection = 'p'
+
+class PC(models.Model):
+    name = models.CharField(maxlength=20)
+    b = models.ForeignKey(PB)
+
+    def __str__(self):
+        return self.name
+
+    class Meta:
+        db_connection = 'p'
+
+# connection q
+class QA(models.Model):
+    name = models.CharField(maxlength=20)
+
+    def __str__(self):
+        return self.name
+
+    class Meta:
+        db_connection = 'q'
+
+class QB(models.Model):
+    name = models.CharField(maxlength=20)
+    a = models.ForeignKey(QA)
+    
+    def __str__(self):
+        return self.name
+
+    class Meta:
+        db_connection = 'q'
+
+# many-many
+class QC(models.Model):
+    name = models.CharField(maxlength=20)
+    
+    def __str__(self):
+        return self.name
+
+    class Meta:
+        db_connection = 'q'
+
+class QD(models.Model):
+    name = models.CharField(maxlength=20)
+    qcs = models.ManyToManyField(QC)
+    
+    def __str__(self):
+        return self.name
+
+    class Meta:
+        db_connection = 'q'
+
+API_TESTS = """
+
+# models can use the default connection or a named connection
+
+# FIXME: DA is already installed
+# models can be installed individually
+
+# FIXME: oracle can't do ddl in a transaction, so this is going to have to
+# be reworked to create new test databases and such as runtests does, which
+# means its going to have to move to othertests
+
+>>> from django.db import transaction
+>>> transaction.enter_transaction_management()
+>>> transaction.managed(True)
+>>> QA._default_manager.install()
+([BoundStatement('CREATE TABLE "manager_schema_manipulation_qa" (...);')], [])
+>>> QB._default_manager.install()
+([BoundStatement('CREATE TABLE "manager_schema_manipulation_qb" (...);')], [])
+>>> list(QA.objects.all())
+[]
+>>> QA(name="something").save()
+>>> QA.objects.all()
+[<QA: something>]
+
+# models with un-installable dependencies will return a list of
+# pending statements; these are bound to the model's connection and should
+# be executed after all other models have been created
+
+>>> result = PA._default_manager.install()
+>>> result
+([BoundStatement('CREATE TABLE "manager_schema_manipulation_pa" (...);'), BoundStatement('CREATE INDEX manager_schema_manipulation_pa_c_id ON "manager_schema_manipulation_pa" ("c_id");')], [BoundStatement('ALTER TABLE "manager_schema_manipulation_pa" ADD CONSTRAINT "c_id_referencing_manager_schema_manipulation_pc_id" FOREIGN KEY ("c_id") REFERENCES "manager_schema_manipulation_pc" ("id");')])
+
+# Many-to-many statements can be generated at any time, but should not be 
+# executed until all models in the app have been created.
+
+>>> QC._default_manager.install()
+([BoundStatement('CREATE TABLE "manager_schema_manipulation_qc" (...);')], [])
+>>> QD._default_manager.install()
+([BoundStatement('CREATE TABLE "manager_schema_manipulation_qd" (...);')], [BoundStatement('CREATE TABLE "manager_schema_manipulation_qd_qcs" (...);')])
+
+"""
  

