Django

Code

Changeset 8296

Show
Ignore:
Timestamp:
08/11/08 07:11:25 (4 months ago)
Author:
russellm
Message:

Fixed #5461 -- Refactored the database backend code to use classes for the creation and introspection modules. Introduces a new validation module for DB-specific validation. This is a backwards incompatible change; see the wiki for details.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/contrib/gis/db/backend/mysql/creation.py

    r8219 r8296  
    1 from django.test.utils import create_test_db 
    21 
    32def create_spatial_db(test=True, verbosity=1, autoclobber=False): 
    43    if not test: raise NotImplementedError('This uses `create_test_db` from test/utils.py') 
    5     create_test_db(verbosity, autoclobber) 
     4    from django.db import connection 
     5    connection.creation.create_test_db(verbosity, autoclobber) 
  • django/trunk/django/contrib/gis/db/backend/oracle/creation.py

    r8219 r8296  
    1 from django.db.backends.oracle.creation import create_test_db 
    21 
    32def create_spatial_db(test=True, verbosity=1, autoclobber=False): 
    43    "A wrapper over the Oracle `create_test_db` routine." 
    54    if not test: raise NotImplementedError('This uses `create_test_db` from db/backends/oracle/creation.py') 
    6     from django.conf import settings 
    75    from django.db import connection 
    8     create_test_db(settings, connection, verbosity, autoclobber) 
     6    connection.creation.create_test_db(verbosity, autoclobber) 
  • django/trunk/django/contrib/gis/db/backend/postgis/creation.py

    r8219 r8296  
    22from django.core.management import call_command 
    33from django.db import connection 
    4 from django.test.utils import _set_autocommit, TEST_DATABASE_PREFIX 
     4from django.db.backends.creation import TEST_DATABASE_PREFIX 
    55import os, re, sys 
    66 
     
    3939    if settings.DATABASE_USER: 
    4040        create_sql += ' OWNER %s' % settings.DATABASE_USER 
    41          
     41 
    4242    cursor = connection.cursor() 
    43     _set_autocommit(connection) 
     43    connection.creation.set_autocommit(connection) 
    4444 
    4545    try: 
     
    5959            raise Exception('Spatial Database Creation canceled.') 
    6060foo = _create_with_cursor 
    61      
     61 
    6262created_regex = re.compile(r'^createdb: database creation failed: ERROR:  database ".+" already exists') 
    6363def _create_with_shell(db_name, verbosity=1, autoclobber=False): 
    6464    """ 
    65     If no spatial database already exists, then using a cursor will not work.   
    66      Thus, a `createdb` command will be issued through the shell to bootstrap  
     65    If no spatial database already exists, then using a cursor will not work. 
     66     Thus, a `createdb` command will be issued through the shell to bootstrap 
    6767     creation of the spatial database. 
    6868    """ 
     
    8484                drop_cmd = 'dropdb %s%s' % (options, db_name) 
    8585                status, output = getstatusoutput(drop_cmd) 
    86                 if status != 0:  
     86                if status != 0: 
    8787                    raise Exception('Could not drop database %s: %s' % (db_name, output)) 
    8888                if verbosity >= 1: print 'Creating new spatial database...' 
     
    103103 
    104104    # Getting the spatial database name 
    105     if test:  
     105    if test: 
    106106        db_name = get_spatial_db(test=True) 
    107107        _create_with_cursor(db_name, verbosity=verbosity, autoclobber=autoclobber) 
    108     else:  
     108    else: 
    109109        db_name = get_spatial_db() 
    110110        _create_with_shell(db_name, verbosity=verbosity, autoclobber=autoclobber) 
     
    126126    # Syncing the database 
    127127    call_command('syncdb', verbosity=verbosity, interactive=interactive) 
    128      
     128 
    129129def drop_db(db_name=False, test=False): 
    130130    """ 
     
    152152def get_spatial_db(test=False): 
    153153    """ 
    154     Returns the name of the spatial database.  The 'test' keyword may be set  
     154    Returns the name of the spatial database.  The 'test' keyword may be set 
    155155     to return the test spatial database name. 
    156156    """ 
     
    168168def load_postgis_sql(db_name, verbosity=1): 
    169169    """ 
    170     This routine loads up the PostGIS SQL files lwpostgis.sql and  
     170    This routine loads up the PostGIS SQL files lwpostgis.sql and 
    171171     spatial_ref_sys.sql. 
    172172    """ 
     
    174174    # Getting the path to the PostGIS SQL 
    175175    try: 
    176         # POSTGIS_SQL_PATH may be placed in settings to tell GeoDjango where the  
     176        # POSTGIS_SQL_PATH may be placed in settings to tell GeoDjango where the 
    177177        #  PostGIS SQL files are located.  This is especially useful on Win32 
    178178        #  platforms since the output of pg_config looks like "C:/PROGRA~1/..". 
     
    194194    options = get_cmd_options(db_name) 
    195195    cmd_fmt = 'psql %s-f "%%s"' % options 
    196      
     196 
    197197    # Now trying to load up the PostGIS functions 
    198198    cmd = cmd_fmt % lwpostgis_file 
     
    212212    #  of the spatial_ref_sys and geometry_columns tables is always 
    213213    #  the postgres user, regardless of how the db is created. 
    214     if os.name == 'nt': set_permissions(db_name)  
    215      
     214    if os.name == 'nt': set_permissions(db_name) 
     215 
    216216def set_permissions(db_name): 
    217217    """ 
  • django/trunk/django/contrib/gis/management/commands/inspectdb.py

    r8219 r8296  
    88 
    99class Command(InspectCommand): 
    10      
     10 
    1111    # Mapping from lower-case OGC type to the corresponding GeoDjango field. 
    1212    geofield_mapping = {'point' : 'PointField', 
     
    2222    def geometry_columns(self): 
    2323        """ 
    24         Returns a datastructure of metadata information associated with the  
     24        Returns a datastructure of metadata information associated with the 
    2525        `geometry_columns` (or equivalent) table. 
    2626        """ 
    2727        # The `geo_cols` is a dictionary data structure that holds information 
    28         # about any geographic columns in the database.  
     28        # about any geographic columns in the database. 
    2929        geo_cols = {} 
    3030        def add_col(table, column, coldata): 
     
    4848            # On MySQL have to get all table metadata before hand; this means walking through 
    4949            # each table and seeing if any column types are spatial.  Can't detect this with 
    50             # `cursor.description` (what the introspection module does) because all spatial types  
     50            # `cursor.description` (what the introspection module does) because all spatial types 
    5151            # have the same integer type (255 for GEOMETRY). 
    5252            from django.db import connection 
     
    6868    def handle_inspection(self): 
    6969        "Overloaded from Django's version to handle geographic database tables." 
    70         from django.db import connection, get_introspection_module 
     70        from django.db import connection 
    7171        import keyword 
    7272 
    73         introspection_module = get_introspection_module() 
     73        geo_cols = self.geometry_columns() 
    7474 
    75         geo_cols = self.geometry_columns() 
    76         
    7775        table2model = lambda table_name: table_name.title().replace('_', '') 
    7876 
     
    8987        yield 'from django.contrib.gis.db import models' 
    9088        yield '' 
    91         for table_name in introspection_module.get_table_list(cursor): 
     89        for table_name in connection.introspection.get_table_list(cursor): 
    9290            # Getting the geographic table dictionary. 
    9391            geo_table = geo_cols.get(table_name, {}) 
     
    9593            yield 'class %s(models.Model):' % table2model(table_name) 
    9694            try: 
    97                 relations = introspection_module.get_relations(cursor, table_name) 
     95                relations = connection.introspection.get_relations(cursor, table_name) 
    9896            except NotImplementedError: 
    9997                relations = {} 
    10098            try: 
    101                 indexes = introspection_module.get_indexes(cursor, table_name) 
     99                indexes = connection.introspection.get_indexes(cursor, table_name) 
    102100            except NotImplementedError: 
    103101                indexes = {} 
    104             for i, row in enumerate(introspection_module.get_table_description(cursor, table_name)): 
     102            for i, row in enumerate(connection.introspection.get_table_description(cursor, table_name)): 
    105103                att_name, iatt_name = row[0].lower(), row[0] 
    106104                comment_notes = [] # Holds Field notes, to be displayed in a Python comment. 
     
    134132                    else: 
    135133                        try: 
    136                             field_type = introspection_module.DATA_TYPES_REVERSE[row[1]] 
     134                            field_type = connection.introspection.data_types_reverse[row[1]] 
    137135                        except KeyError: 
    138136                            field_type = 'TextField' 
    139137                            comment_notes.append('This field type is a guess.') 
    140138 
    141                     # This is a hook for DATA_TYPES_REVERSE to return a tuple of 
     139                    # This is a hook for data_types_reverse to return a tuple of 
    142140                    # (field_type, extra_params_dict). 
    143141                    if type(field_type) is tuple: 
  • django/trunk/django/core/management/commands/dbshell.py

    r7294 r8296  
    77 
    88    def handle_noargs(self, **options): 
    9         from django.db import runshell 
    10         runshell() 
     9        from django.db import connection 
     10        connection.client.runshell() 
  • django/trunk/django/core/management/commands/inspectdb.py

    r7294 r8296  
    1414 
    1515    def handle_inspection(self): 
    16         from django.db import connection, get_introspection_module 
     16        from django.db import connection 
    1717        import keyword 
    18  
    19         introspection_module = get_introspection_module() 
    2018 
    2119        table2model = lambda table_name: table_name.title().replace('_', '') 
     
    3331        yield 'from django.db import models' 
    3432        yield '' 
    35         for table_name in introspection_module.get_table_list(cursor): 
     33        for table_name in connection.introspection.get_table_list(cursor): 
    3634            yield 'class %s(models.Model):' % table2model(table_name) 
    3735            try: 
    38                 relations = introspection_module.get_relations(cursor, table_name) 
     36                relations = connection.introspection.get_relations(cursor, table_name) 
    3937            except NotImplementedError: 
    4038                relations = {} 
    4139            try: 
    42                 indexes = introspection_module.get_indexes(cursor, table_name) 
     40                indexes = connection.introspection.get_indexes(cursor, table_name) 
    4341            except NotImplementedError: 
    4442                indexes = {} 
    45             for i, row in enumerate(introspection_module.get_table_description(cursor, table_name)): 
     43            for i, row in enumerate(connection.introspection.get_table_description(cursor, table_name)): 
    4644                att_name = row[0].lower() 
    4745                comment_notes = [] # Holds Field notes, to be displayed in a Python comment. 
     
    6664                else: 
    6765                    try: 
    68                         field_type = introspection_module.DATA_TYPES_REVERSE[row[1]] 
     66                        field_type = connection.introspection.data_types_reverse[row[1]] 
    6967                    except KeyError: 
    7068                        field_type = 'TextField' 
  • django/trunk/django/core/management/commands/syncdb.py

    r8133 r8296  
    2222        from django.db import connection, transaction, models 
    2323        from django.conf import settings 
    24         from django.core.management.sql import table_names, installed_models, sql_model_create, sql_for_pending_references, many_to_many_sql_for_model, custom_sql_for_model, sql_indexes_for_model, emit_post_sync_signal 
     24        from django.core.management.sql import custom_sql_for_model, emit_post_sync_signal 
    2525 
    2626        verbosity = int(options.get('verbosity', 1)) 
     
    5151        cursor = connection.cursor() 
    5252 
    53         if connection.features.uses_case_insensitive_names: 
    54             table_name_converter = lambda x: x.upper() 
    55         else: 
    56             table_name_converter = lambda x: x 
    57         # Get a list of all existing database tables, so we know what needs to 
    58         # be added. 
    59         tables = [table_name_converter(name) for name in table_names()] 
    60  
    6153        # Get a list of already installed *models* so that references work right. 
    62         seen_models = installed_models(tables) 
     54        tables = connection.introspection.table_names() 
     55        seen_models = connection.introspection.installed_models(tables) 
    6356        created_models = set() 
    6457        pending_references = {} 
     
    7265                if verbosity >= 2: 
    7366                    print "Processing %s.%s model" % (app_name, model._meta.object_name) 
    74                 if table_name_converter(model._meta.db_table) in tables: 
     67                if connection.introspection.table_name_converter(model._meta.db_table) in tables: 
    7568                    continue 
    76                 sql, references = sql_model_create(model, self.style, seen_models) 
     69                sql, references = connection.creation.sql_create_model(model, self.style, seen_models) 
    7770                seen_models.add(model) 
    7871                created_models.add(model) 
     
    8073                    pending_references.setdefault(refto, []).extend(refs) 
    8174                    if refto in seen_models: 
    82                         sql.extend(sql_for_pending_references(refto, self.style, pending_references)) 
    83                 sql.extend(sql_for_pending_references(model, self.style, pending_references)) 
     75                        sql.extend(connection.creation.sql_for_pending_references(refto, self.style, pending_references)) 
     76                sql.extend(connection.creation.sql_for_pending_references(model, self.style, pending_references)) 
    8477                if verbosity >= 1: 
    8578                    print "Creating table %s" % model._meta.db_table 
    8679                for statement in sql: 
    8780                    cursor.execute(statement) 
    88                 tables.append(table_name_converter(model._meta.db_table)) 
     81                tables.append(connection.introspection.table_name_converter(model._meta.db_table)) 
    8982 
    9083        # Create the m2m tables. This must be done after all tables have been created 
     
    9588            for model in model_list: 
    9689                if model in created_models: 
    97                     sql = many_to_many_sql_for_model(model, self.style) 
     90                    sql = connection.creation.sql_for_many_to_many(model, self.style) 
    9891                    if sql: 
    9992                        if verbosity >= 2: 
     
    141134            for model in models.get_models(app): 
    142135                if model in created_models: 
    143                     index_sql = sql_indexes_for_model(model, self.style) 
     136                    index_sql = connection.creation.sql_indexes_for_model(model, self.style) 
    144137                    if index_sql: 
    145138                        if verbosity >= 1: 
  • django/trunk/django/core/management/commands/testserver.py

    r8046 r8296  
    1919    def handle(self, *fixture_labels, **options): 
    2020        from django.core.management import call_command 
    21         from django.test.utils import create_test_db 
     21        from django.db import connection 
    2222 
    2323        verbosity = int(options.get('verbosity', 1)) 
     
    2525 
    2626        # Create a test database. 
    27         db_name = create_test_db(verbosity=verbosity) 
     27        db_name = connection.creation.create_test_db(verbosity=verbosity) 
    2828 
    2929        # Import the fixture data into the test database. 
  • django/trunk/django/core/management/sql.py

    r8223 r8296  
    88    from sets import Set as set   # Python 2.3 fallback 
    99 
    10 def table_names(): 
    11     "Returns a list of all table names that exist in the database." 
    12     from django.db import connection, get_introspection_module 
    13     cursor = connection.cursor() 
    14     return set(get_introspection_module().get_table_list(cursor)) 
    15  
    16 def django_table_names(only_existing=False): 
    17     """ 
    18     Returns a list of all table names that have associated Django models and 
    19     are in INSTALLED_APPS. 
    20  
    21     If only_existing is True, the resulting list will only include the tables 
    22     that actually exist in the database. 
    23     """ 
    24     from django.db import models 
    25     tables = set() 
    26     for app in models.get_apps(): 
    27         for model in models.get_models(app): 
    28             tables.add(model._meta.db_table) 
    29             tables.update([f.m2m_db_table() for f in model._meta.local_many_to_many]) 
    30     if only_existing: 
    31         tables = [t for t in tables if t in table_names()] 
    32     return tables 
    33  
    34 def installed_models(table_list): 
    35     "Returns a set of all models that are installed, given a list of existing table names." 
    36     from django.db import connection, models 
    37     all_models = [] 
    38     for app in models.get_apps(): 
    39         for model in models.get_models(app): 
    40             all_models.append(model) 
    41     if connection.features.uses_case_insensitive_names: 
    42         converter = lambda x: x.upper() 
    43     else: 
    44         converter = lambda x: x 
    45     return set([m for m in all_models if converter(m._meta.db_table) in map(converter, table_list)]) 
    46  
    47 def sequence_list(): 
    48     "Returns a list of information about all DB sequences for all models in all apps." 
    49     from django.db import models 
    50  
    51     apps = models.get_apps() 
    52     sequence_list = [] 
    53  
    54     for app in apps: 
    55         for model in models.get_models(app): 
    56             for f in model._meta.local_fields: 
    57                 if isinstance(f, models.AutoField): 
    58                     sequence_list.append({'table': model._meta.db_table, 'column': f.column}) 
    59                     break # Only one AutoField is allowed per model, so don't bother continuing. 
    60  
    61             for f in model._meta.local_many_to_many: 
    62                 sequence_list.append({'table': f.m2m_db_table(), 'column': None}) 
    63  
    64     return sequence_list 
    65  
    6610def sql_create(app, style): 
    6711    "Returns a list of the CREATE TABLE SQL statements for the given app." 
    68     from django.db import models 
     12    from django.db import connection, models 
    6913    from django.conf import settings 
    7014 
     
    8226    app_models = models.get_models(app) 
    8327    final_output = [] 
    84     known_models = set([model for model in installed_models(table_names()) if model not in app_models]) 
     28    tables = connection.introspection.table_names() 
     29    known_models = set([model for model in connection.introspection.installed_models(tables) if model not in app_models]) 
    8530    pending_references = {} 
    8631 
    8732    for model in app_models: 
    88         output, references = sql_model_create(model, style, known_models) 
     33        output, references = connection.creation.sql_create_model(model, style, known_models) 
    8934        final_output.extend(output) 
    9035        for refto, refs in references.items(): 
    9136            pending_references.setdefault(refto, []).extend(refs) 
    9237            if refto in known_models: 
    93                 final_output.extend(sql_for_pending_references(refto, style, pending_references)) 
    94         final_output.extend(sql_for_pending_references(model, style, pending_references)) 
     38                final_output.extend(connection.creation.sql_for_pending_references(refto, style, pending_references)) 
     39        final_output.extend(connection.creation.sql_for_pending_references(model, style, pending_references)) 
    9540        # Keep track of the fact that we've created the table for this model. 
    9641        known_models.add(model) 
     
    9843    # Create the many-to-many join tables. 
    9944    for model in app_models: 
    100         final_output.extend(many_to_many_sql_for_model(model, style)) 
     45        final_output.extend(connection.creation.sql_for_many_to_many(model, style)) 
    10146 
    10247    # Handle references to tables that are from other apps 
     
    10752        for model in not_installed_models: 
    10853            alter_sql.extend(['-- ' + sql for sql in 
    109                 sql_for_pending_references(model, style, pending_references)]) 
     54                connection.creation.sql_for_pending_references(model, style, pending_references)]) 
    11055        if alter_sql: 
    11156            final_output.append('-- The following references should be added but depend on non-existent tables:') 
     
    11661def sql_delete(app, style): 
    11762    "Returns a list of the DROP TABLE SQL statements for the given app." 
    118     from django.db import connection, models, get_introspection_module 
     63    from django.db import connection, models 
    11964    from django.db.backends.util import truncate_name 
    12065    from django.contrib.contenttypes import generic 
    121     introspection = get_introspection_module() 
    12266 
    12367    # This should work even if a connection isn't available 
     
    12973    # Figure out which tables already exist 
    13074    if cursor: 
    131         table_names = introspection.get_table_list(cursor) 
     75        table_names = connection.introspection.get_table_list(cursor) 
    13276    else: 
    13377        table_names = [] 
    134     if connection.features.uses_case_insensitive_names: 
    135         table_name_converter = lambda x: x.upper() 
    136     else: 
    137         table_name_converter = lambda x: x 
    138  
    139     output = [] 
    140     qn = connection.ops.quote_name 
     78 
     79    output = [] 
    14180 
    14281    # Output DROP TABLE statements for standard application tables. 
     
    14685    app_models = models.get_models(app) 
    14786    for model in app_models: 
    148         if cursor and table_name_converter(model._meta.db_table) in table_names: 
     87        if cursor and connection.introspection.table_name_converter(model._meta.db_table) in table_names: 
    14988            # The table exists, so it needs to be dropped 
    15089            opts = model._meta 
     
    15695 
    15796    for model in app_models: 
    158         if cursor and table_name_converter(model._meta.db_table) in table_names: 
    159             # Drop the table now 
    160             output.append('%s %s;' % (style.SQL_KEYWORD('DROP TABLE'), 
    161                 style.SQL_TABLE(qn(model._meta.db_table)))) 
    162             if connection.features.supports_constraints and model in references_to_delete: 
    163                 for rel_class, f in references_to_delete[model]: 
    164                     table = rel_class._meta.db_table 
    165                     col = f.column 
    166                     r_table = model._meta.db_table 
    167                     r_col = model._meta.get_field(f.rel.field_name).column 
    168                     r_name = '%s_refs_%s_%x' % (col, r_col, abs(hash((table, r_table)))) 
    169                     output.append('%s %s %s %s;' % \ 
    170                         (style.SQL_KEYWORD('ALTER TABLE'), 
    171                         style.SQL_TABLE(qn(table)), 
    172                         style.SQL_KEYWORD(connection.ops.drop_foreignkey_sql()), 
    173                         style.SQL_FIELD(truncate_name(r_name, connection.ops.max_name_length())))) 
    174                 del references_to_delete[model] 
    175             if model._meta.has_auto_field: 
    176                 ds = connection.ops.drop_sequence_sql(model._meta.db_table) 
    177                 if ds: 
    178                     output.append(ds) 
     97        if connection.introspection.table_name_converter(model._meta.db_table) in table_names: 
     98            output.extend(connection.creation.sql_destroy_model(model, references_to_delete, style)) 
    17999 
    180100    # Output DROP TABLE statements for many-to-many tables. 
     
    182102        opts = model._meta 
    183103        for f in opts.local_many_to_many: 
    184             if not f.creates_table: 
    185                 continue 
    186             if cursor and table_name_converter(f.m2m_db_table()) in table_names: 
    187                 output.append("%s %s;" % (style.SQL_KEYWORD('DROP TABLE'), 
    188                     style.SQL_TABLE(qn(f.m2m_db_table())))) 
    189                 ds = connection.ops.drop_sequence_sql("%s_%s" % (model._meta.db_table, f.column)) 
    190                 if ds: 
    191                     output.append(ds) 
     104            if cursor and connection.introspection.table_name_converter(f.m2m_db_table()) in table_names: 
     105                output.extend(connection.creation.sql_destroy_many_to_many(model, f, style)) 
    192106 
    193107    app_label = app_models[0]._meta.app_label 
     
    214128    from django.db import connection 
    215129    if only_django: 
    216         tables = django_table_names() 
     130        tables = connection.introspection.django_table_names() 
    217131    else: 
    218         tables = table_names() 
    219     statements = connection.ops.sql_flush(style, tables, sequence_list()) 
     132        tables = connection.introspection.table_names() 
     133    statements = connection.ops.sql_flush(style, tables, connection.introspection.sequence_list()) 
    220134    return statements 
    221135 
     
    235149def sql_indexes(app, style): 
    236150    "Returns a list of the CREATE INDEX SQL statements for all models in the given app." 
    237     from django.db import models 
     151    from django.db import connection, models 
    238152    output = [] 
    239153    for model in models.get_models(app): 
    240         output.extend(sql_indexes_for_model(model, style)) 
     154        output.extend(connection.creation.sql_indexes_for_model(model, style)) 
    241155    return output 
    242156 
     
    244158    "Returns a list of CREATE TABLE SQL, initial-data inserts, and CREATE INDEX SQL for the given module." 
    245159    return sql_create(app, style) + sql_custom(app, style) + sql_indexes(app, style) 
    246  
    247 def sql_model_create(model, style, known_models=set()): 
    248     """ 
    249     Returns the SQL required to create a single model, as a tuple of: 
    250         (list_of_sql, pending_references_dict) 
    251     """ 
    252     from django.db import connection, models 
    253  
    254     opts = model._meta 
    255     final_output = [] 
    256     table_output = [] 
    257     pending_references = {} 
    258     qn = connection.ops.quote_name 
    259     inline_references = connection.features.inline_fk_references 
    260     for f in opts.local_fields: 
    261         col_type = f.db_type() 
    262         tablespace = f.db_tablespace or opts.db_tablespace 
    263         if col_type is None: 
    264             # Skip ManyToManyFields, because they're not represented as 
    265             # database columns in this table. 
    266             continue 
    267         # Make the definition (e.g. 'foo VARCHAR(30)') for this field. 
    268         field_output = [style.SQL_FIELD(qn(f.column)), 
    269             style.SQL_COLTYPE(col_type)] 
    270         field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or ''))) 
    271         if f.primary_key: 
    272             field_output.append(style.SQL_KEYWORD('PRIMARY KEY')) 
    273         elif f.unique: 
    274             field_output.append(style.SQL_KEYWORD('UNIQUE')) 
    275         if tablespace and connection.features.supports_tablespaces and f.unique: 
    276             # We must specify the index tablespace inline, because we 
    277             # won't be generating a CREATE INDEX statement for this field. 
    278             field_output.append(connection.ops.tablespace_sql(tablespace, inline=True)) 
    279         if f.rel: 
    280             if inline_references and f.rel.to in known_models: 
    281                 field_output.append(style.SQL_KEYWORD('REFERENCES') + ' ' + \ 
    282                     style.SQL_TABLE(qn(f.rel.to._meta.db_table)) + ' (' + \ 
    283                     style.SQL_FIELD(qn(f.rel.to._meta.get_field(f.rel.field_name).column)) + ')' + 
    284                     connection.ops.deferrable_sql() 
    285                 ) 
    286             else: 
    287                 # We haven't yet created the table to which this field 
    288                 # is related, so save it for later. 
    289                 pr = pending_references.setdefault(f.rel.to, []).append((model, f)) 
    290         table_output.append(' '.join(field_output)) 
    291     if opts.order_with_respect_to: 
    292         table_output.append(style.SQL_FIELD(qn('_order')) + ' ' + \ 
    293             style.SQL_COLTYPE(models.IntegerField().db_type()) + ' ' + \ 
    294             style.SQL_KEYWORD('NULL')) 
    295     for field_constraints in opts.unique_together: 
    296         table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \ 
    297             ", ".join([style.SQL_FIELD(qn(opts.get_field(f).column)) for f in field_constraints])) 
    298  
    299     full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(qn(opts.db_table)) + ' ('] 
    300     for i, line in enumerate(table_output): # Combine and add commas. 
    301         full_statement.append('    %s%s' % (line, i < len(table_output)-1 and ',' or '')) 
    302     full_statement.append(')') 
    303     if opts.db_tablespace and connection.features.supports_tablespaces: 
    304         full_statement.append(connection.ops.tablespace_sql(opts.db_tablespace)) 
    305     full_statement.append(';') 
    306     final_output.append('\n'.join(full_statement)) 
    307  
    308     if opts.has_auto_field: 
    309         # Add any extra SQL needed to support auto-incrementing primary keys. 
    310         auto_column = opts.auto_field.db_column or opts.auto_field.name 
    311         autoinc_sql = connection.ops.autoinc_sql(opts.db_table, auto_column) 
    312         if autoinc_sql: 
    313             for stmt in autoinc_sql: 
    314                 final_output.append(stmt) 
    315  
    316     return final_output, pending_references 
    317  
    318 def sql_for_pending_references(model, style, pending_references): 
    319     """ 
    320     Returns any ALTER TABLE statements to add constraints after the fact. 
    321     """ 
    322     from django.db import connection 
    323     from django.db.backends.util import truncate_name 
    324  
    325     qn = connection.ops.quote_name 
    326     final_output = [] 
    327     if connection.features.supports_constraints: 
    328         opts = model._meta 
    329         if model in pending_references: 
    330             for rel_class, f in pending_references[model]: 
    331                 rel_opts = rel_class._meta 
    332                 r_table = rel_opts.db_table 
    333                 r_col = f.column 
    334                 table = opts.db_table 
    335                 col = opts.get_field(f.rel.field_name).column 
    336                 # For MySQL, r_name must be unique in the first 64 characters. 
    337                 # So we are careful with character usage here. 
    338                 r_name = '%s_refs_%s_%x' % (r_col, col, abs(hash((r_table, table)))) 
    339                 final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \ 
    340                     (qn(r_table), truncate_name(r_name, connection.ops.max_name_length()), 
    341                     qn(r_col), qn(table), qn(col), 
    342                     connection.ops.deferrable_sql())) 
    343             del pending_references[model] 
    344     return final_output 
    345  
    346 def many_to_many_sql_for_model(model, style): 
    347     from django.db import connection, models 
    348     from django.contrib.contenttypes import generic 
    349     from django.db.backends.util import truncate_name 
    350  
    351     opts = model._meta 
    352     final_output = [] 
    353     qn = connection.ops.quote_name 
    354     inline_references = connection.features.inline_fk_references 
    355     for f in opts.local_many_to_many: 
    356         if f.creates_table: 
    357             tablespace = f.db_tablespace or opts.db_tablespace 
    358             if tablespace and connection.features.supports_tablespaces:  
    359                 tablespace_sql = ' ' + connection.ops.tablespace_sql(tablespace, inline=True) 
    360             else: 
    361                 tablespace_sql = '' 
    362             table_output = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + \ 
    363                 style.SQL_TABLE(qn(f.m2m_db_table())) + ' ('] 
    364             table_output.append('    %s %s %s%s,' % 
    365                 (style.SQL_FIELD(qn('id')), 
    366                 style.SQL_COLTYPE(models.AutoField(primary_key=True).db_type()), 
    367                 style.SQL_KEYWORD('NOT NULL PRIMARY KEY'), 
    368                 tablespace_sql)) 
    369             if inline_references: 
    370                 deferred = [] 
    371                 table_output.append('    %s %s %s %s (%s)%s,' % 
    372                     (style.SQL_FIELD(qn(f.m2m_column_name())), 
    373                     style.SQL_COLTYPE(models.ForeignKey(model).db_type()), 
    374                     style.SQL_KEYWORD('NOT NULL REFERENCES'), 
    375                     style.SQL_TABLE(qn(opts.db_table)), 
    376                     style.SQL_FIELD(qn(opts.pk.column)), 
    377                     connection.ops.deferrable_sql())) 
    378                 table_output.append('    %s %s %s %s (%s)%s,' % 
    379                     (style.SQL_FIELD(qn(f.m2m_reverse_name())), 
    380                     style.SQL_COLTYPE(models.ForeignKey(f.rel.to).db_type()), 
    381                     style.SQL_KEYWORD('NOT NULL REFERENCES'), 
    382                     style.SQL_TABLE(qn(f.rel.to._meta.db_table)), 
    383                     style.SQL_FIELD(qn(f.rel.to._meta.pk.column)), 
    384                     connection.ops.deferrable_sql())) 
    385             else: 
    386                 table_output.append('    %s %s %s,' % 
    387                     (style.SQL_FIELD(qn(f.m2m_column_name())), 
    388                     style.SQL_COLTYPE(models.ForeignKey(model).db_type()), 
    389                     style.SQL_KEYWORD('NOT NULL'))) 
    390                 table_output.append('    %s %s %s,' % 
    391                     (style.SQL_FIELD(qn(f.m2m_reverse_name())), 
    392                     style.SQL_COLTYPE(models.ForeignKey(f.rel.to).db_type()), 
    393                     style.SQL_KEYWORD('NOT NULL'))) 
    394                 deferred = [ 
    395                     (f.m2m_db_table(), f.m2m_column_name(), opts.db_table, 
    396                         opts.pk.column), 
    397                     ( f.m2m_db_table(), f.m2m_reverse_name(), 
    398                         f.rel.to._meta.db_table, f.rel.to._meta.pk.column) 
    399                     ] 
    400             table_output.append('    %s (%s, %s)%s' % 
    401                 (style.SQL_KEYWORD('UNIQUE'), 
    402                 style.SQL_FIELD(qn(f.m2m_column_name())), 
    403                 style.SQL_FIELD(qn(f.m2m_reverse_name())), 
    404                 tablespace_sql)) 
    405             table_output.append(')') 
    406             if opts.db_tablespace and connection.features.supports_tablespaces: 
    407                 # f.db_tablespace is only for indices, so ignore its value here. 
    408                 table_output.append(connection.ops.tablespace_sql(opts.db_tablespace)) 
    409             table_output.append(';') 
    410             final_output.append('\n'.join(table_output)) 
    411  
    412