MultipleDatabaseSupport: p5.diff

File p5.diff, 52.8 KB (added by jpellerin@…, 9 years ago)

Updated patch including transaction support

  • django/db/models/base.py

    === django/db/models/base.py
    ==================================================================
     
    77from django.db.models.related import RelatedObject
    88from django.db.models.query import orderlist2sql, delete_objects
    99from django.db.models.options import Options, AdminOptions
    10 from django.db import connection, backend, transaction
     10from django.db import transaction
    1111from django.db.models import signals
    1212from django.db.models.loading import register_models
    1313from django.dispatch import dispatcher
     
    3838                new_class._meta.parents.extend(base._meta.parents)
    3939
    4040        model_module = sys.modules[new_class.__module__]
    41 
     41       
    4242        if getattr(new_class._meta, 'app_label', None) is None:
    4343            # Figure out the app_label by looking one level up.
    4444            # For 'django.contrib.sites.models', this would be 'sites'.
     
    154154    def save(self):
    155155        dispatcher.send(signal=signals.pre_save, sender=self.__class__, instance=self)
    156156
     157        info = self._meta.connection_info
     158        connection = info.connection
     159        backend = info.backend
    157160        non_pks = [f for f in self._meta.fields if not f.primary_key]
    158161        cursor = connection.cursor()
    159162
     
    201204                     backend.get_pk_default_value()))
    202205            if self._meta.has_auto_field and not pk_set:
    203206                setattr(self, self._meta.pk.attname, backend.get_last_insert_id(cursor, self._meta.db_table, self._meta.pk.column))
    204         transaction.commit_unless_managed()
     207        transaction.commit_unless_managed([connection])
    205208
    206209        # Run any post-save hooks.
    207210        dispatcher.send(signal=signals.post_save, sender=self.__class__, instance=self)
     
    272275        return dict(field.choices).get(value, value)
    273276
    274277    def _get_next_or_previous_by_FIELD(self, field, is_next, **kwargs):
     278        backend = self._meta.connection_info.backend
    275279        op = is_next and '>' or '<'
    276280        where = '(%s %s %%s OR (%s = %%s AND %s.%s %s %%s))' % \
    277281            (backend.quote_name(field.column), op, backend.quote_name(field.column),
     
    286290            raise self.DoesNotExist, "%s matching query does not exist." % self.__class__._meta.object_name
    287291
    288292    def _get_next_or_previous_in_order(self, is_next):
     293        backend = self._meta.connection_info.backend
    289294        cachename = "__%s_order_cache" % is_next
    290295        if not hasattr(self, cachename):
    291296            op = is_next and '>' or '<'
     
    374379        rel = rel_field.rel.to
    375380        m2m_table = rel_field.m2m_db_table()
    376381        this_id = self._get_pk_val()
     382        info = self._meta.connection_info
     383        connection = info.connection
     384        backend = info.backend
    377385        cursor = connection.cursor()
    378386        cursor.execute("DELETE FROM %s WHERE %s = %%s" % \
    379387            (backend.quote_name(m2m_table),
     
    383391            backend.quote_name(rel_field.m2m_column_name()),
    384392            backend.quote_name(rel_field.m2m_reverse_name()))
    385393        cursor.executemany(sql, [(this_id, i) for i in id_list])
    386         transaction.commit_unless_managed()
     394        transaction.commit_unless_managed([connection])
    387395
    388396############################################
    389397# HELPER FUNCTIONS (CURRIED MODEL METHODS) #
     
    392400# ORDERING METHODS #########################
    393401
    394402def method_set_order(ordered_obj, self, id_list):
     403    connection_info = ordered_obj.connection_info
     404    connection = info.connection
     405    backend = info.backend
     406   
    395407    cursor = connection.cursor()
    396408    # Example: "UPDATE poll_choices SET _order = %s WHERE poll_id = %s AND id = %s"
    397409    sql = "UPDATE %s SET %s = %%s WHERE %s = %%s AND %s = %%s" % \
     
    400412        backend.quote_name(ordered_obj.pk.column))
    401413    rel_val = getattr(self, ordered_obj.order_with_respect_to.rel.field_name)
    402414    cursor.executemany(sql, [(i, rel_val, j) for i, j in enumerate(id_list)])
    403     transaction.commit_unless_managed()
     415    transaction.commit_unless_managed([connection])
    404416
    405417def method_get_order(ordered_obj, self):
     418    connection_info = ordered_obj.connection_info
     419    connection = info.connection
     420    backend = info.backend
    406421    cursor = connection.cursor()
    407422    # Example: "SELECT id FROM poll_choices WHERE poll_id = %s ORDER BY _order"
    408423    sql = "SELECT %s FROM %s WHERE %s = %%s ORDER BY %s" % \
  • django/db/models/manager.py

    === django/db/models/manager.py
    ==================================================================
     
    9999    def values(self, *args, **kwargs):
    100100        return self.get_query_set().values(*args, **kwargs)
    101101
     102    #######################
     103    # SCHEMA MANIPULATION #
     104    #######################
     105
     106    def install(self, initial_data=False):
     107        """Install my model's table, indexes and (if requested) initial data.
     108
     109        Returns a 2-tuple of the lists of statements executed and
     110        statements pending. Pending statements are those that could
     111        not yet be executed, such as foreign key constraints for
     112        tables that don't exist at install time.
     113        """
     114        creator = self.model._meta.connection_info.get_creation_module()
     115        run, pending = creator.get_create_table(self.model)
     116        run += creator.get_create_indexes(self.model)
     117        pending += creator.get_create_many_to_many(self.model)
     118        if initial_data:
     119            run += creator.get_initialdata(self.model)
     120
     121        executed = []
     122        for statement in run:
     123            statement.execute()
     124            executed.append(statement)
     125        return (executed, pending)
     126
     127    def load_initial_data(self):
     128        """Load initial data for my model into the database."""
     129        pass
     130
     131    def drop(self):
     132        """Drop my model's table."""
     133        pass
     134
     135    # Future...
     136   
     137    def add_column(self, column):
     138        """Add a column to my model's table"""
     139        pass
     140
     141    def drop_column(self, column):
     142        """Drop a column from my model's table"""
     143        pass
     144       
     145
    102146class ManagerDescriptor(object):
    103147    # This class ensures managers aren't accessible via model instances.
    104148    # For example, Poll.objects works, but poll_obj.objects raises AttributeError.
  • django/db/models/options.py

    === django/db/models/options.py
    ==================================================================
     
    11from django.conf import settings
     2from django.db import connection_info, connections
    23from django.db.models.related import RelatedObject
    34from django.db.models.fields.related import ManyToManyRel
    45from django.db.models.fields import AutoField, FieldDoesNotExist
     
    1112# Calculate the verbose_name by converting from InitialCaps to "lowercase with spaces".
    1213get_verbose_name = lambda class_name: re.sub('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))', ' \\1', class_name).lower().strip()
    1314
    14 DEFAULT_NAMES = ('verbose_name', 'db_table', 'ordering',
     15DEFAULT_NAMES = ('verbose_name', 'db_connection', 'db_table', 'ordering',
    1516                 'unique_together', 'permissions', 'get_latest_by',
    1617                 'order_with_respect_to', 'app_label')
    1718
     
    2021        self.fields, self.many_to_many = [], []
    2122        self.module_name, self.verbose_name = None, None
    2223        self.verbose_name_plural = None
     24        self.db_connection = None
    2325        self.db_table = ''
    2426        self.ordering = []
    2527        self.unique_together =  []
     
    5658                raise TypeError, "'class Meta' got invalid attribute(s): %s" % ','.join(meta_attrs.keys())
    5759        else:
    5860            self.verbose_name_plural = self.verbose_name + 's'
     61        #
    5962        del self.meta
    6063
    6164    def _prepare(self, model):
     
    8891    def __repr__(self):
    8992        return '<Options for %s>' % self.object_name
    9093
     94    def get_connection_info(self):
     95        if self.db_connection:
     96            return connections[self.db_connection]
     97        return connection_info
     98    connection_info = property(get_connection_info)
     99           
     100    def get_connection(self):
     101        """Get the database connection for this object's model"""
     102        return self.get_connection_info().connection
     103    connection = property(get_connection)
     104   
    91105    def get_field(self, name, many_to_many=True):
    92106        "Returns the requested field by name. Raises FieldDoesNotExist on error."
    93107        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
    ==================================================================
     
    88_app_list = None # Cache of installed apps.
    99_app_models = {} # Dictionary of models against app label
    1010                 # Each value is a dictionary of model name: model class
    11 
     11_app_model_order = {} # Dictionary of models against app label
     12                      # Each value is a list of model names, in the order in
     13                      # which the models were created
    1214def get_apps():
    1315    "Returns a list of all installed modules that contain models."
    1416    global _app_list
     
    3537            return __import__(app_name, '', '', ['models']).models
    3638    raise ImproperlyConfigured, "App with label %s could not be found" % app_label
    3739
    38 def get_models(app_mod=None):
     40def get_models(app_mod=None, creation_order=False):
    3941    """
    4042    Given a module containing models, returns a list of the models. Otherwise
    41     returns a list of all installed models.
     43    returns a list of all installed models. In either case, if creation_order
     44    is true, return the models sorted into the same order in which they
     45    were created.
    4246    """
    4347    app_list = get_apps() # Run get_apps() to populate the _app_list cache. Slightly hackish.
    4448    if app_mod:
    45         return _app_models.get(app_mod.__name__.split('.')[-2], {}).values()
     49        app_label = app_mod.__name__.split('.')[-2]
     50        app_models = _app_models.get(app_label, {})
     51        if creation_order:
     52            return [ app_models[name]
     53                     for name in _app_model_order.get(app_label, []) ]
     54        return app_models.values()
    4655    else:
    4756        model_list = []
    4857        for app_mod in app_list:
     
    7584        model_name = model._meta.object_name.lower()
    7685        model_dict = _app_models.setdefault(app_label, {})
    7786        model_dict[model_name] = model
     87        model_list = _app_model_order.setdefault(app_label, [])
     88        model_list.append(model_name)
  • django/db/models/query.py

    === django/db/models/query.py
    ==================================================================
     
    1 from django.db import backend, connection, transaction
     1from django.db import backend, connection, connections, transaction
    22from django.db.models.fields import DateField, FieldDoesNotExist
    33from django.db.models import signals
    44from django.dispatch import dispatcher
     
    158158        # undefined, so we convert it to a list of tuples.
    159159        extra_select = self._select.items()
    160160
    161         cursor = connection.cursor()
     161        cursor = self.model._meta.connection.cursor()
    162162        select, sql, params = self._get_sql_clause()
    163163        cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
    164164        fill_cache = self._select_related
     
    178178
    179179    def count(self):
    180180        "Performs a SELECT COUNT() and returns the number of records as an integer."
     181
     182        info = self.model._meta.connection_info
     183        backend = info.backend
     184        connection = info.connection
     185       
    181186        counter = self._clone()
    182187        counter._order_by = ()
    183188        counter._offset = None
     
    505510            columns = [f.column for f in self.model._meta.fields]
    506511            field_names = [f.attname for f in self.model._meta.fields]
    507512
     513        info = self.model._meta.connection_info
     514        backend = info.backend
     515        connection = info.connection
    508516        cursor = connection.cursor()
    509517        select, sql, params = self._get_sql_clause()
    510518        select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns]
     
    524532class DateQuerySet(QuerySet):
    525533    def iterator(self):
    526534        from django.db.backends.util import typecast_timestamp
     535
     536        info = self.model._meta.connection_info
     537        backend = info.backend
     538        connection = info.connection
     539       
    527540        self._order_by = () # Clear this because it'll mess things up otherwise.
    528541        if self._field.null:
    529542            self._where.append('%s.%s IS NOT NULL' % \
     
    616629        where2 = ['(NOT (%s))' % " AND ".join(where)]
    617630        return tables, joins, where2, params
    618631
    619 def get_where_clause(lookup_type, table_prefix, field_name, value):
     632def get_where_clause(opts, lookup_type, table_prefix, field_name, value):
     633    backend = opts.connection_info.backend
     634   
    620635    if table_prefix.endswith('.'):
    621636        table_prefix = backend.quote_name(table_prefix[:-1])+'.'
    622637    field_name = backend.quote_name(field_name)
     
    745760    intermediate_table = None
    746761    join_required = False
    747762
     763    info = current_opts.connection_info
     764    backend = info.backend
     765    connection = info.connection
     766
    748767    name = path.pop(0)
    749768    # Has the primary key been requested? If so, expand it out
    750769    # to be the name of the current class' primary key
     
    872891        else:
    873892            column = field.column
    874893
    875         where.append(get_where_clause(clause, current_table + '.', column, value))
     894        where.append(get_where_clause(current_opts, clause, current_table + '.', column, value))
    876895        params.extend(field.get_db_prep_lookup(clause, value))
    877896
    878897    return tables, joins, where, params
     
    882901    ordered_classes = seen_objs.keys()
    883902    ordered_classes.reverse()
    884903
    885     cursor = connection.cursor()
    886904
    887905    for cls in ordered_classes:
     906
     907        info = cls._meta.connection_info
     908        backend = info.backend
     909        connection = info.connection
     910        cursor = connection.cursor()
     911       
    888912        seen_objs[cls] = seen_objs[cls].items()
    889913        seen_objs[cls].sort()
    890914
     
    919943
    920944    # Now delete the actual data
    921945    for cls in ordered_classes:
     946
     947        info = cls._meta.connection_info
     948        backend = info.backend
     949        connection = info.connection
     950        cursor = connection.cursor()
     951
    922952        seen_objs[cls].reverse()
    923953        pk_list = [pk for pk,instance in seen_objs[cls]]
    924954        for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
  • django/db/__init__.py

    === django/db/__init__.py
    ==================================================================
     
    1 from django.conf import settings
     1from django.conf import settings, UserSettingsHolder
    22from django.core import signals
     3from django.core.exceptions import ImproperlyConfigured
    34from django.dispatch import dispatcher
    45
    56__all__ = ('backend', 'connection', 'DatabaseError')
     
    78if not settings.DATABASE_ENGINE:
    89    settings.DATABASE_ENGINE = 'dummy'
    910
    10 try:
    11     backend = __import__('django.db.backends.%s.base' % settings.DATABASE_ENGINE, '', '', [''])
    12 except ImportError, e:
    13     # The database backend wasn't found. Display a helpful error message
    14     # listing all possible database backends.
    15     from django.core.exceptions import ImproperlyConfigured
    16     import os
    17     backend_dir = os.path.join(__path__[0], 'backends')
    18     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')]
    19     available_backends.sort()
    20     if settings.DATABASE_ENGINE not in available_backends:
    21         raise ImproperlyConfigured, "%r isn't an available database backend. vailable options are: %s" % \
    22             (settings.DATABASE_ENGINE, ", ".join(map(repr, available_backends)))
    23     else:
    24         raise # If there's some other error, this must be an error in Django itself.
     11class ConnectionInfo(object):
     12    """Encapsulates all information about a db connection:
     13     - connection
     14     - DatabaseError
     15     - backend
     16     - get_introspection_module
     17     - get_creation_module
     18     - runshell
     19    """
     20    def __init__(self, connection, DatabaseError, backend,
     21                 get_introspection_module, get_creation_module, runshell):
     22        self.connection = connection
     23        self.DatabaseError = DatabaseError
     24        self.backend = backend
     25        self.get_introspection_module = get_introspection_module
     26        self.get_creation_module = get_creation_module
     27        self.runshell = runshell
    2528
    26 get_introspection_module = lambda: __import__('django.db.backends.%s.introspection' % settings.DATABASE_ENGINE, '', '', [''])
    27 get_creation_module = lambda: __import__('django.db.backends.%s.creation' % settings.DATABASE_ENGINE, '', '', [''])
    28 runshell = lambda: __import__('django.db.backends.%s.client' % settings.DATABASE_ENGINE, '', '', ['']).runshell()
     29    def __repr__(self):
     30        return "Connection: %r (ENGINE=%s NAME=%s)" \
     31               % (self.connection,
     32                  self.connection.settings.DATABASE_ENGINE,
     33                  self.connection.settings.DATABASE_NAME)
    2934
    30 connection = backend.DatabaseWrapper()
    31 DatabaseError = backend.DatabaseError
     35def connect(settings=settings):
     36    """Connect to the database specified in the given settings.
     37    """
     38    try:
     39        backend = __import__('django.db.backends.%s.base' % settings.DATABASE_ENGINE, '', '', [''])
     40    except ImportError, e:
     41        # The database backend wasn't found. Display a helpful error message
     42        # listing all possible database backends.
     43        import os
     44        backend_dir = os.path.join(__path__[0], 'backends')
     45        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')]
     46        available_backends.sort()
     47        if settings.DATABASE_ENGINE not in available_backends:
     48            raise ImproperlyConfigured, "%r isn't an available database backend. vailable options are: %s" % \
     49                (settings.DATABASE_ENGINE, ", ".join(map(repr, available_backends)))
     50        else:
     51            raise # If there's some other error, this must be an error in Django itself.
    3252
    33 # Register an event that closes the database connection
    34 # when a Django request is finished.
    35 dispatcher.connect(connection.close, signal=signals.request_finished)
    3653
     54    get_introspection_module = lambda: __import__('django.db.backends.%s.introspection' % settings.DATABASE_ENGINE, '', '', [''])
     55    get_creation_module = lambda: __import__('django.db.backends.%s.creation' % settings.DATABASE_ENGINE, '', '', [''])
     56    runshell = lambda: __import__('django.db.backends.%s.client' % settings.DATABASE_ENGINE, '', '', ['']).runshell()
     57
     58    connection = backend.DatabaseWrapper(settings)
     59    DatabaseError = backend.DatabaseError
     60
     61    return ConnectionInfo(connection, DatabaseError, backend,
     62                          get_introspection_module, get_creation_module,
     63                          runshell)
     64
     65connection_info = connect(settings)
     66
     67# backwards compatibility
     68(connection, DatabaseError, backend, get_introspection_module,
     69 get_creation_module, runshell) = (connection_info.connection,
     70                                   connection_info.DatabaseError,
     71                                   connection_info.backend,
     72                                   connection_info.get_introspection_module,
     73                                   connection_info.get_creation_module,
     74                                   connection_info.runshell)
     75
     76
     77class LazyConnectionManager(object):
     78    """Manages named connections lazily, instantiating them as
     79    they are requested.
     80    """
     81
     82    def __init__(self):
     83        self._connections = {}
     84
     85    def __iter__(self):
     86        return self._connections.keys()
     87
     88    def __getattr__(self, attr):
     89        return getattr(self._connections, attr)
     90
     91    def __getitem__(self, k):
     92        try:
     93            return self._connections[k]
     94        except KeyError:
     95            try:
     96                self.connect(k)
     97                return self._connections[k]
     98            except KeyError:
     99                raise ImproperlyConfigured, \
     100                      "No database connection '%s' has been configured" % k
     101
     102    def __setattr(self, attr, val):
     103        setattr(self._connections, attr, val)
     104           
     105    def connect(self, name):
     106        from django.conf import settings
     107        try:
     108            database = settings.DATABASES[name]
     109            try:
     110                database.DATABASE_ENGINE
     111            except AttributeError:
     112                # assume its a dict and convert to settings instance
     113                holder = UserSettingsHolder(settings)
     114                for k, v in database.items():
     115                    setattr(holder, k, v)
     116                    database = holder
     117                settings.DATABASES[name] = database
     118            self._connections[name] = connect(database)
     119        except AttributeError:
     120            # no named connections to set up
     121            pass
     122connections = LazyConnectionManager()
     123
    37124# Register an event that resets connection.queries
    38125# when a Django request is started.
    39 def reset_queries():
     126def reset_queries(connection=connection):
    40127    connection.queries = []
    41128dispatcher.connect(reset_queries, signal=signals.request_started)
    42129
     
    46133    from django.db import transaction
    47134    transaction.rollback_unless_managed()
    48135dispatcher.connect(_rollback_on_exception, signal=signals.got_request_exception)
     136
     137# Register an event that closes the database connection
     138# when a Django request is finished.
     139dispatcher.connect(connection.close, signal=signals.request_finished)
     140
  • django/db/backends/ado_mssql/creation.py

    === django/db/backends/ado_mssql/creation.py
    ==================================================================
     
     1from django.db.backends.ansi.creation import SchemaBuilder
     2
    13DATA_TYPES = {
    24    'AutoField':         'int IDENTITY (1, 1)',
    35    'BooleanField':      'bit',
     
    2426    'URLField':          'varchar(200)',
    2527    'USStateField':      'varchar(2)',
    2628}
     29
     30_builder = SchemaBuilder()
     31get_create_table = _builder.get_create_table
     32get_create_indexes = _builder.get_create_indexes
     33get_create_many_to_many = _builder.get_create_many_to_many
     34get_initialdata = _builder.get_initialdata
  • django/db/backends/postgresql/base.py

    === django/db/backends/postgresql/base.py
    ==================================================================
     
    2121    from django.utils._threading_local import local
    2222
    2323class DatabaseWrapper(local):
    24     def __init__(self):
     24    def __init__(self, settings):
     25        self.settings = settings
    2526        self.connection = None
    2627        self.queries = []
    2728
    2829    def cursor(self):
    29         from django.conf import settings
     30        settings = self.settings
    3031        if self.connection is None:
    3132            if settings.DATABASE_NAME == '':
    3233                from django.core.exceptions import ImproperlyConfigured
  • django/db/backends/postgresql/creation.py

    === django/db/backends/postgresql/creation.py
    ==================================================================
     
     1from django.db.backends.ansi.creation import SchemaBuilder
     2
    13# This dictionary maps Field objects to their associated PostgreSQL column
    24# types, as strings. Column-type strings can contain format strings; they'll
    35# be interpolated against the values of Field.__dict__ before being output.
     
    2830    'URLField':          'varchar(200)',
    2931    'USStateField':      'varchar(2)',
    3032}
     33
     34_builder = SchemaBuilder()
     35get_create_table = _builder.get_create_table
     36get_create_indexes = _builder.get_create_indexes
     37get_create_many_to_many = _builder.get_create_many_to_many
     38get_initialdata = _builder.get_initialdata
  • django/db/backends/sqlite3/base.py

    === django/db/backends/sqlite3/base.py
    ==================================================================
     
    3434    from django.utils._threading_local import local
    3535
    3636class DatabaseWrapper(local):
    37     def __init__(self):
     37    def __init__(self, settings):
     38        self.settings = settings
    3839        self.connection = None
    3940        self.queries = []
    4041
    4142    def cursor(self):
    42         from django.conf import settings
     43        settings = self.settings
    4344        if self.connection is None:
    4445            self.connection = Database.connect(settings.DATABASE_NAME,
    4546                detect_types=Database.PARSE_DECLTYPES | Database.PARSE_COLNAMES)
  • django/db/backends/sqlite3/creation.py

    === django/db/backends/sqlite3/creation.py
    ==================================================================
     
     1from django.db.backends.ansi.creation import SchemaBuilder
     2
    13# SQLite doesn't actually support most of these types, but it "does the right
    24# thing" given more verbose field definitions, so leave them as is so that
    35# schema inspection is more useful.
     
    2729    'URLField':                     'varchar(200)',
    2830    'USStateField':                 'varchar(2)',
    2931}
     32
     33_builder = SchemaBuilder()
     34get_create_table = _builder.get_create_table
     35get_create_indexes = _builder.get_create_indexes
     36get_create_many_to_many = _builder.get_create_many_to_many
     37get_initialdata = _builder.get_initialdata
  • django/db/backends/mysql/creation.py

    === django/db/backends/mysql/creation.py
    ==================================================================
     
     1from django.db.backends.ansi.creation import SchemaBuilder
     2
    13# This dictionary maps Field objects to their associated MySQL column
    24# types, as strings. Column-type strings can contain format strings; they'll
    35# be interpolated against the values of Field.__dict__ before being output.
     
    2830    'URLField':          'varchar(200)',
    2931    'USStateField':      'varchar(2)',
    3032}
     33
     34_builder = SchemaBuilder()
     35get_create_table = _builder.get_create_table
     36get_create_indexes = _builder.get_create_indexes
     37get_create_many_to_many = _builder.get_create_many_to_many
     38get_initialdata = _builder.get_initialdata
  • django/db/backends/oracle/creation.py

    === django/db/backends/oracle/creation.py
    ==================================================================
     
     1from django.db.backends.ansi.creation import SchemaBuilder
     2
    13DATA_TYPES = {
    24    'AutoField':         'number(38)',
    35    'BooleanField':      'number(1)',
     
    2426    'URLField':          'varchar(200)',
    2527    'USStateField':      'varchar(2)',
    2628}
     29
     30_builder = SchemaBuilder()
     31get_create_table = _builder.get_create_table
     32get_create_indexes = _builder.get_create_indexes
     33get_create_many_to_many = _builder.get_create_many_to_many
     34get_initialdata = _builder.get_initialdata
  • django/db/backends/ansi/__init__.py

    === django/db/backends/ansi/__init__.py
    ==================================================================
     
     1pass
  • django/db/backends/ansi/creation.py

    === django/db/backends/ansi/creation.py
    ==================================================================
     
     1"""ANSISQL schema manipulation functions and classes
     2"""
     3from django.db import models
     4
     5# FIXME correct handling of styles,
     6# allow style object to be passed in
     7class dummy:
     8    def __getattr__(self, attr):
     9        return lambda x: x
     10
     11class BoundStatement(object):
     12    """Represents an SQL statement that is to be executed, at some point in
     13    the future, using a specific database connection.
     14    """
     15    def __init__(self, sql, connection):
     16        self.sql = sql
     17        self.connection = connection
     18
     19    def execute(self):
     20        cursor = self.connection.cursor()
     21        cursor.execute(self.sql)
     22
     23    def __repr__(self):
     24        return "BoundStatement(%r)" % self.sql
     25
     26    def __str__(self):
     27        return self.sql
     28
     29    def __eq__(self, other):
     30        return self.sql == other.sql and self.connection == other.connection
     31
     32class SchemaBuilder(object):
     33    """Basic ANSI SQL schema element builder. Instances of this class may be
     34    used to construct SQL expressions that create or drop schema elements such
     35    as tables, indexes and (for those backends that support them) foreign key
     36    or other constraints.
     37    """
     38    def __init__(self):
     39        self.models_already_seen = []
     40       
     41    def get_create_table(self, model, style=dummy()):
     42        """Construct and return the SQL expression(s) needed to create the
     43        table for the given model, and any constraints on that
     44        table. The return value is a 2-tuple. The first element of the tuple
     45        is a list of BoundStatements that may be executed immediately. The
     46        second is a list of BoundStatements representing constraints that
     47        can't be executed immediately because (for instance) the referent
     48        table does not exist.
     49        """
     50        if model in self.models_already_seen:
     51            return (None, None, None)
     52        self.models_already_seen.append(model)
     53       
     54        opts = model._meta
     55        info = opts.connection_info
     56        backend = info.backend
     57        quote_name = backend.quote_name
     58        data_types = info.get_creation_module().DATA_TYPES
     59        table_output = []
     60        pending_references = {}
     61        pending = [] # actual pending statements to execute
     62        for f in opts.fields:
     63            if isinstance(f, models.ForeignKey):
     64                rel_field = f.rel.get_related_field()
     65                data_type = self.get_rel_data_type(rel_field)
     66            else:
     67                rel_field = f
     68                data_type = f.get_internal_type()
     69            col_type = data_types[data_type]
     70            if col_type is not None:
     71                # Make the definition (e.g. 'foo VARCHAR(30)') for this field.
     72                field_output = [style.SQL_FIELD(quote_name(f.column)),
     73                    style.SQL_COLTYPE(col_type % rel_field.__dict__)]
     74                field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or '')))
     75                if f.unique:
     76                    field_output.append(style.SQL_KEYWORD('UNIQUE'))
     77                if f.primary_key:
     78                    field_output.append(style.SQL_KEYWORD('PRIMARY KEY'))
     79                if f.rel:
     80                    if f.rel.to in self.models_already_seen:
     81                        field_output.append(style.SQL_KEYWORD('REFERENCES') + ' ' + \
     82                            style.SQL_TABLE(quote_name(f.rel.to._meta.db_table)) + ' (' + \
     83                            style.SQL_FIELD(quote_name(f.rel.to._meta.get_field(f.rel.field_name).column)) + ')'
     84                        )
     85                    else:
     86                        # We haven't yet created the table to which this field
     87                        # is related, so save it for later.
     88                        pr = pending_references.setdefault(f.rel.to, []).append(f)
     89                table_output.append(' '.join(field_output))
     90        if opts.order_with_respect_to:
     91            table_output.append(style.SQL_FIELD(quote_name('_order')) + ' ' + \
     92                style.SQL_COLTYPE(data_types['IntegerField']) + ' ' + \
     93                style.SQL_KEYWORD('NULL'))
     94        for field_constraints in opts.unique_together:
     95            table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \
     96                ", ".join([quote_name(style.SQL_FIELD(opts.get_field(f).column)) for f in field_constraints]))
     97
     98        full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(quote_name(opts.db_table)) + ' (']
     99        for i, line in enumerate(table_output): # Combine and add commas.
     100            full_statement.append('    %s%s' % (line, i < len(table_output)-1 and ',' or ''))
     101        full_statement.append(');')
     102
     103        create = [BoundStatement('\n'.join(full_statement), opts.connection)]
     104
     105        if (pending_references and
     106            backend.supports_constraints):
     107            for rel_class, cols in pending_references.items():
     108                for f in cols:
     109                    rel_opts = rel_class._meta
     110                    r_table = rel_opts.db_table
     111                    r_col = f.column
     112                    table = opts.db_table
     113                    col = opts.get_field(f.rel.field_name).column
     114                    sql = style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s);' % \
     115                        (quote_name(table),
     116                        quote_name('%s_referencing_%s_%s' % (r_col, r_table, col)),
     117                        quote_name(r_col), quote_name(r_table), quote_name(col))
     118                    pending.append(BoundStatement(sql, opts.connection))
     119        return (create, pending)   
     120
     121    def get_create_indexes(self, model, style=dummy()):
     122        """Construct and return SQL statements needed to create the indexes for
     123        a model. Returns a list of BoundStatements.
     124        """
     125        info = model._meta.connection_info
     126        backend = info.backend
     127        connection = info.connection
     128        output = []
     129        for f in model._meta.fields:
     130            if f.db_index:
     131                unique = f.unique and 'UNIQUE ' or ''
     132                output.append(
     133                    BoundStatement(
     134                        ' '.join(
     135                            [style.SQL_KEYWORD('CREATE %sINDEX' % unique),
     136                             style.SQL_TABLE('%s_%s' %
     137                                             (model._meta.db_table, f.column)),
     138                             style.SQL_KEYWORD('ON'),
     139                             style.SQL_TABLE(
     140                                    backend.quote_name(model._meta.db_table)),
     141                             "(%s);" % style.SQL_FIELD(
     142                                    backend.quote_name(f.column))]),
     143                        connection)
     144                    )
     145        return output
     146
     147    def get_create_many_to_many(self, model, style=dummy()):
     148        """Construct and return SQL statements needed to create the
     149        tables and relationships for all many-to-many relations
     150        defined in the model. Returns a list of bound statments. Note
     151        that these statements should only be execute after all models
     152        for an app have been created.
     153        """
     154        info = model._meta.connection_info
     155        backend = info.backend
     156        connection = info.connection       
     157        data_types = info.get_creation_module().DATA_TYPES
     158        opts = model._meta
     159        output = []       
     160        for f in opts.many_to_many:
     161            if not isinstance(f.rel, models.GenericRel):
     162                table_output = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + \
     163                    style.SQL_TABLE(backend.quote_name(f.m2m_db_table())) + ' (']
     164                table_output.append('    %s %s %s,' % \
     165                    (style.SQL_FIELD(backend.quote_name('id')),
     166                    style.SQL_COLTYPE(data_types['AutoField']),
     167                    style.SQL_KEYWORD('NOT NULL PRIMARY KEY')))
     168                table_output.append('    %s %s %s %s (%s),' % \
     169                    (style.SQL_FIELD(backend.quote_name(f.m2m_column_name())),
     170                    style.SQL_COLTYPE(data_types[self.get_rel_data_type(opts.pk)] % opts.pk.__dict__),
     171                    style.SQL_KEYWORD('NOT NULL REFERENCES'),
     172                    style.SQL_TABLE(backend.quote_name(opts.db_table)),
     173                    style.SQL_FIELD(backend.quote_name(opts.pk.column))))
     174                table_output.append('    %s %s %s %s (%s),' % \
     175                    (style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name())),
     176                    style.SQL_COLTYPE(data_types[self.get_rel_data_type(f.rel.to._meta.pk)] % f.rel.to._meta.pk.__dict__),
     177                    style.SQL_KEYWORD('NOT NULL REFERENCES'),
     178                    style.SQL_TABLE(backend.quote_name(f.rel.to._meta.db_table)),
     179                    style.SQL_FIELD(backend.quote_name(f.rel.to._meta.pk.column))))
     180                table_output.append('    %s (%s, %s)' % \
     181                    (style.SQL_KEYWORD('UNIQUE'),
     182                    style.SQL_FIELD(backend.quote_name(f.m2m_column_name())),
     183                    style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name()))))
     184                table_output.append(');')
     185                output.append(BoundStatement('\n'.join(table_output),
     186                                             connection))
     187        return output
     188
     189   
     190    def get_initialdata(self, model, style=dummy()):
     191        return [] # FIXME
     192
     193    def get_rel_data_type(self, f):
     194        return (f.get_internal_type() in ('AutoField', 'PositiveIntegerField',
     195                                          'PositiveSmallIntegerField')) \
     196                                          and 'IntegerField' \
     197                                          or f.get_internal_type()
  • django/db/transaction.py

    === django/db/transaction.py
    ==================================================================
     
    1616    import thread
    1717except ImportError:
    1818    import dummy_thread as thread
    19 from django.db import connection
     19from django import db
    2020from django.conf import settings
    2121
    2222class TransactionManagementError(Exception):
     
    116116    Puts the transaction manager into a manual state: managed transactions have
    117117    to be committed explicitly by the user. If you switch off transaction
    118118    management and there is a pending commit/rollback, the data will be
    119     commited.
     119    commited. Note that managed state applies across all connections.
    120120    """
    121121    thread_ident = thread.get_ident()
    122122    top = state.get(thread_ident, None)
    123123    if top:
    124124        top[-1] = flag
    125125        if not flag and is_dirty():
    126             connection._commit()
     126            for cx in all_connections():
     127                cx._commit()
    127128            set_clean()
    128129    else:
    129130        raise TransactionManagementError("This code isn't under transaction management")
    130131
    131 def commit_unless_managed():
     132def commit_unless_managed(connections=None):
    132133    """
    133134    Commits changes if the system is not in managed transaction mode.
    134135    """
    135136    if not is_managed():
    136         connection._commit()
     137        if connections is None:
     138            connections = all_connections()
     139        else:
     140            connections = ensure_connections(connections)
     141        for cx in connections:
     142            cx._commit()
    137143    else:
    138144        set_dirty()
    139145
    140 def rollback_unless_managed():
     146def rollback_unless_managed(connections=None):
    141147    """
    142148    Rolls back changes if the system is not in managed transaction mode.
    143149    """
    144150    if not is_managed():
    145         connection._rollback()
     151        if connections is None:
     152            connections = all_connections()
     153        for cx in connections:
     154            cx._rollback()
    146155    else:
    147156        set_dirty()
    148157
    149 def commit():
     158def commit(connections=None):
    150159    """
    151160    Does the commit itself and resets the dirty flag.
    152161    """
    153     connection._commit()
     162    if connections is None:
     163        connections = all_connections()
     164    else:
     165        connections = ensure_connections(connections)
     166    for cx in connections:
     167        cx._commit()
    154168    set_clean()
    155169
    156 def rollback():
     170def rollback(connections=None):
    157171    """
    158172    This function does the rollback itself and resets the dirty flag.
    159173    """
    160     connection._rollback()
     174    if connections is None:
     175        connections = all_connections()
     176    else:
     177        connections = ensure_connections(connections)
     178    for cx in connections:
     179        cx._rollback()
    161180    set_clean()
    162181
    163182##############
     
    179198            leave_transaction_management()
    180199    return _autocommit
    181200
    182 def commit_on_success(func):
     201def commit_on_success(func, connections=None):
    183202    """
    184203    This decorator activates commit on response. This way, if the view function
    185204    runs successfully, a commit is made; if the viewfunc produces an exception,
     
    198217                raise
    199218            else:
    200219                if is_dirty():
    201                     commit()
     220                    commit(connections)
    202221            return res
    203222        finally:
    204223            leave_transaction_management()
     
    220239            leave_transaction_management()
    221240
    222241    return _commit_manually
     242
     243###########
     244# HELPERS #
     245###########
     246
     247def all_connections():
     248    return [db.connection] + [ c.connection
     249                               for c in db.connections.values() ]
     250
     251def ensure_connections(val):
     252    connections = []
     253    if isinstance(val, basestring):
     254        val = [val]
     255    try:
     256        iter(val)
     257    except:
     258        val = [val]
     259    for cx in val:
     260        if hasattr(cx, 'cursor'):
     261            connections.append(cx)
     262        elif hasattr(cx, 'connection'):
     263            connections.append(cx.connection)
     264        elif isinstance(cx, basestring):
     265            connections.append(db.connections[cx].connection)
     266    return connections
     267       
  • tests/modeltests/multiple_databases/__init__.py

    === tests/modeltests/multiple_databases/__init__.py
    ==================================================================
     
     1pass
  • tests/modeltests/multiple_databases/models.py

    === tests/modeltests/multiple_databases/models.py
    ==================================================================
     
     1"""
     2N. Using multiple database connections
     3
     4Django normally uses only a single database connection. However, support is
     5available for connecting to any number of different, named databases on a
     6per-model-class level.
     7
     8Please note that this test uses hard-coded databases, and will fail
     9unless sqlite3 is install and /tmp is writeable. It also will
     10currently fail unless the two temp databases are removed by hand
     11between runs. It also side-effects the settings as set in the
     12DJANGO_SETTINGS_MODULE. All of these are undesirable, and mean that
     13this test is almost certainly going to have to move to othertests.
     14
     15It would be really nice if the testrunner supported setup and teardown.
     16
     17"""
     18
     19# models can define which connections they will use
     20from django.db import models
     21
     22class Artist(models.Model):
     23    name = models.CharField(maxlength=100)
     24    alive = models.BooleanField(default=True)
     25
     26    def __str__(self):
     27        return self.name
     28
     29    class Meta:
     30        db_connection = 'a'
     31       
     32class Widget(models.Model):
     33    code = models.CharField(maxlength=10, unique=True)
     34    weight = models.IntegerField()
     35
     36    def __str__(self):
     37        return self.code
     38   
     39    class Meta:
     40        db_connection = 'b'
     41
     42
     43# but they don't have to
     44class Vehicle(models.Model):
     45    make = models.CharField(maxlength=20)
     46    model = models.CharField(maxlength=20)
     47    year = models.IntegerField()
     48
     49    def __str__(self):
     50        return "%d %s %s" % (self.year, self.make, self.model)
     51
     52API_TESTS = """
     53
     54# Connections are established lazily, when requested by name.
     55
     56>>> from django.conf import settings
     57>>> settings.DATABASES = {
     58...     'a': { 'DATABASE_ENGINE': 'sqlite3',
     59...            'DATABASE_NAME': '/tmp/dba.db'
     60...     },
     61...     'b': { 'DATABASE_ENGINE': 'sqlite3',
     62...            'DATABASE_NAME': '/tmp/dbb.db'
     63...     }}
     64>>> from django.db import connections
     65
     66# connections[database] holds a tuple of connection, DatabaseError, backend,
     67# get_introspection_module, get_creation_module, and runshell
     68
     69>>> connections['a']
     70Connection: <django.db.backends.sqlite3.base.DatabaseWrapper object at ...> (ENGINE=sqlite3 NAME=/tmp/dba.db)
     71>>> connections['b']
     72Connection: <django.db.backends.sqlite3.base.DatabaseWrapper object at ...> (ENGINE=sqlite3 NAME=/tmp/dbb.db)
     73
     74# Invalid connection names raise ImproperlyConfigured
     75
     76>>> connections['bad']
     77Traceback (most recent call last):
     78    ...
     79ImproperlyConfigured: No database connection 'bad' has been configured
     80
     81# For the time being, we have to install the models by hand
     82
     83>>> from django.core import management
     84>>> artist_sql = ''.join(management._get_sql_model_create(Artist)[0])
     85>>> cursor = Artist._meta.connection.cursor()
     86>>> cursor.execute(artist_sql)
     87>>> widget_sql = ''.join(management._get_sql_model_create(Widget)[0])
     88>>> cursor = Widget._meta.connection.cursor()
     89>>> cursor.execute(widget_sql)
     90
     91# Then we can play with them
     92
     93>>> a = Artist(name="Paul Klee", alive=False)
     94>>> a.save()
     95>>> a._meta.connection.settings.DATABASE_NAME
     96'/tmp/dba.db'
     97>>> w = Widget(code='100x2r', weight=1000)
     98>>> w.save()
     99>>> w._meta.connection.settings.DATABASE_NAME
     100'/tmp/dbb.db'
     101>>> v = Vehicle(make='Chevy', model='Camaro', year='1966')
     102>>> v.save()
     103>>> v._meta.connection.settings.DATABASE_NAME
     104':memory:'
     105
     106# Managers use their models' connections
     107
     108>>> artists = Artist.objects.all()
     109>>> list(artists)
     110[<Artist: Paul Klee>]
     111>>> artists[0]._meta.connection.settings.DATABASE_NAME
     112'/tmp/dba.db'
     113>>> widgets = Widget.objects.all()
     114>>> list(widgets)
     115[<Widget: 100x2r>]
     116>>> widgets[0]._meta.connection.settings.DATABASE_NAME
     117'/tmp/dbb.db'
     118>>> vehicles = Vehicle.objects.all()
     119>>> list(vehicles)
     120[<Vehicle: 1966 Chevy Camaro>]
     121>>> vehicles[0]._meta.connection.settings.DATABASE_NAME
     122':memory:'
     123
     124# When not using transaction management, model save will commit only
     125# for the model's connection
     126
     127>>> from django.db import transaction
     128>>> transaction.enter_transaction_management()
     129>>> a = Artist(name="Joan Miro", alive=False)
     130>>> w = Widget(code="99rbln", weight=1)
     131>>> a.save()
     132
     133# only connection 'a' is committed, so if we rollback all connections
     134# we'll forget the new Widget.
     135
     136>>> transaction.rollback()
     137>>> list(Artist.objects.all())
     138[<Artist: Paul Klee>, <Artist: Joan Miro>]
     139>>> list(Widget.objects.all())
     140[<Widget: 100x2r>]
     141
     142# Managed transaction state applies across all connections.
     143
     144>>> transaction.managed(True)
     145
     146# When managed, just as when using a single connection, updates are
     147# not committed until a commit is issued.
     148
     149>>> a = Artist(name="Pablo Picasso", alive=False)
     150>>> a.save()
     151>>> w = Widget(code="99rbln", weight=1)
     152>>> w.save()
     153>>> v = Vehicle(make='Pontiac', model='Fiero', year='1987')
     154>>> v.save()
     155
     156# The connections argument may be passed to commit, rollback, and the
     157# commit_on_success decorator as a keyword argument, or the first (for commit
     158# and rollback) or second (for the decorator) positional argument. It
     159# may be passed as a single connection info object, a single
     160# connection object, a single connection name, or a list of connection
     161# info objects, connection objects, or connection names.
     162
     163>>> transaction.commit(connections['b'])
     164>>> transaction.commit('b')
     165>>> transaction.commit(['a', 'b'])
     166>>> transaction.commit(connections)
     167
     168# When omitted entirely, the transaction command applies to all
     169# connections. Here we have committed connections 'a' and 'b', but not
     170# the default connection, so the new vehicle is lost on rollback.
     171
     172>>> transaction.rollback()
     173>>> list(Artist.objects.all())
     174[<Artist: Paul Klee>, <Artist: Joan Miro>, <Artist: Pablo Picasso>]
     175>>> list(Widget.objects.all())
     176[<Widget: 100x2r>, <Widget: 99rbln>]
     177>>> list(Vehicle.objects.all())
     178[<Vehicle: 1966 Chevy Camaro>]
     179
     180>>> transaction.leave_transaction_management()
     181
     182"""
  • tests/modeltests/manager_schema_manipulation/__init__.py

    === tests/modeltests/manager_schema_manipulation/__init__.py
    ==================================================================
     
     1pass
  • tests/modeltests/manager_schema_manipulation/models.py

    === tests/modeltests/manager_schema_manipulation/models.py
    ==================================================================
     
     1"""
     2N. Schema manipulations
     3
     4Django uses a model's manager to perform schema manipulations such as
     5creating or dropping the model's table.
     6
     7Please note that your settings file must define DATABASES with names
     8'p' and 'q' for this test.
     9"""
     10
     11from django.db import models
     12
     13# default connection
     14class DA(models.Model):
     15    name = models.CharField(maxlength=20)
     16
     17    def __str__(self):
     18        return self.name
     19   
     20# connection p
     21class PA(models.Model):
     22    name = models.CharField(maxlength=20)
     23    # This creates a cycle in the dependency graph
     24    c = models.ForeignKey('PC', null=True)
     25   
     26    def __str__(self):
     27        return self.name
     28
     29    class Meta:
     30        db_connection = 'p'
     31
     32class PB(models.Model):
     33    name = models.CharField(maxlength=20)
     34    a = models.ForeignKey(PA)
     35
     36    def __str__(self):
     37        return self.name
     38
     39    class Meta:
     40        db_connection = 'p'
     41
     42class PC(models.Model):
     43    name = models.CharField(maxlength=20)
     44    b = models.ForeignKey(PB)
     45
     46    def __str__(self):
     47        return self.name
     48
     49    class Meta:
     50        db_connection = 'p'
     51
     52# connection q
     53class QA(models.Model):
     54    name = models.CharField(maxlength=20)
     55
     56    def __str__(self):
     57        return self.name
     58
     59    class Meta:
     60        db_connection = 'q'
     61
     62class QB(models.Model):
     63    name = models.CharField(maxlength=20)
     64    a = models.ForeignKey(QA)
     65   
     66    def __str__(self):
     67        return self.name
     68
     69    class Meta:
     70        db_connection = 'q'
     71
     72# many-many
     73class QC(models.Model):
     74    name = models.CharField(maxlength=20)
     75   
     76    def __str__(self):
     77        return self.name
     78
     79    class Meta:
     80        db_connection = 'q'
     81
     82class QD(models.Model):
     83    name = models.CharField(maxlength=20)
     84    qcs = models.ManyToManyField(QC)
     85   
     86    def __str__(self):
     87        return self.name
     88
     89    class Meta:
     90        db_connection = 'q'
     91
     92API_TESTS = """
     93
     94# models can use the default connection or a named connection
     95
     96# FIXME: DA is already installed
     97# models can be installed individually
     98
     99# FIXME: oracle can't do ddl in a transaction, so this is going to have to
     100# be reworked to create new test databases and such as runtests does, which
     101# means its going to have to move to othertests
     102
     103>>> from django.db import transaction
     104>>> transaction.enter_transaction_management()
     105>>> transaction.managed(True)
     106>>> QA._default_manager.install()
     107([BoundStatement('CREATE TABLE "manager_schema_manipulation_qa" (...);')], [])
     108>>> QB._default_manager.install()
     109([BoundStatement('CREATE TABLE "manager_schema_manipulation_qb" (...);')], [])
     110>>> list(QA.objects.all())
     111[]
     112>>> QA(name="something").save()
     113>>> QA.objects.all()
     114[<QA: something>]
     115
     116# models with un-installable dependencies will return a list of
     117# pending statements; these are bound to the model's connection and should
     118# be executed after all other models have been created
     119
     120>>> result = PA._default_manager.install()
     121>>> result
     122([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");')])
     123
     124# Many-to-many statements can be generated at any time, but should not be
     125# executed until all models in the app have been created.
     126
     127>>> QC._default_manager.install()
     128([BoundStatement('CREATE TABLE "manager_schema_manipulation_qc" (...);')], [])
     129>>> QD._default_manager.install()
     130([BoundStatement('CREATE TABLE "manager_schema_manipulation_qd" (...);')], [BoundStatement('CREATE TABLE "manager_schema_manipulation_qd_qcs" (...);')])
     131
     132"""
Back to Top