Django

Code

Changeset 5785

Show
Ignore:
Timestamp:
08/01/07 22:28:52 (1 year ago)
Author:
danderson
Message:

schema-evolution:
added new "pk_requires_unique" option to the backend, because sqlite3 requires "UNIQUE" when creating PKs in order to
_actually_ create the constraint.
fixed "get_known_column_flags" introspection for sqlite3
implemented "get_drop_column_sql" for sqlite3 to work around sqlite's lack of DROP COLUMN support
added partial of the sqlite3 unit tests

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/schema-evolution/django/core/management.py

    r5784 r5785  
    171171            style.SQL_COLTYPE(col_type)] 
    172172        field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or ''))) 
    173         if f.unique and (not f.primary_key or backend.allows_unique_and_pk): 
     173        if (f.unique and (not f.primary_key or backend.allows_unique_and_pk)) or (f.primary_key and backend.pk_requires_unique): 
    174174            field_output.append(style.SQL_KEYWORD('UNIQUE')) 
    175175        if f.primary_key: 
     
    570570            col_type = data_types[data_type] 
    571571            if col_type is not None: 
    572 #                field_output = [] 
    573 #                field_output.append('ALTER TABLE') 
    574 #                field_output.append(db_table) 
    575 #                field_output.append('ADD COLUMN') 
    576 #                field_output.append(backend.quote_name(f.column)) 
    577 #                field_output.append(style.SQL_COLTYPE(col_type % rel_field.__dict__)) 
    578 #                field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or ''))) 
    579 #                if f.unique: 
    580 #                    field_output.append(style.SQL_KEYWORD('UNIQUE')) 
    581 #                if f.primary_key: 
    582 #                    field_output.append(style.SQL_KEYWORD('PRIMARY KEY')) 
    583 #                output.append(' '.join(field_output) + ';') 
    584572                output.append( backend.get_add_column_sql( db_table, f.column, style.SQL_COLTYPE(col_type % rel_field.__dict__), f.null, f.unique, f.primary_key ) ) 
    585573    return output 
     
    665653                    column_flags['primary_key']!=f.primary_key: 
    666654                    #column_flags['foreign_key']!=f.foreign_key: 
    667 #                print  
     655#                print 'need to change' 
    668656#                print db_table, f.column, column_flags 
    669657#                print "column_flags['allow_null']!=f.null", column_flags['allow_null']!=f.null 
     
    704692        if f.aka: suspect_fields.difference_update(f.aka) 
    705693    if len(suspect_fields)>0: 
    706         output.append( '-- warning: as the following may cause data loss, it/they must be run manually' ) 
     694        output.append( '-- warning: the following may cause data loss' ) 
    707695        for suspect_field in suspect_fields: 
    708             output.append( '-- '+ backend.get_drop_column_sql( db_table, suspect_field ) ) 
     696            output.extend( backend.get_drop_column_sql( db_table, suspect_field ) ) 
    709697        output.append( '-- end warning' ) 
    710698    return output 
  • django/branches/schema-evolution/django/db/backends/ado_mssql/base.py

    r5734 r5785  
    9292allows_group_by_ordinal = True 
    9393allows_unique_and_pk = True 
     94pk_requires_unique = False 
    9495autoindexes_primary_keys = True 
    9596needs_datetime_string_cast = True 
  • django/branches/schema-evolution/django/db/backends/mysql/base.py

    r5784 r5785  
    137137allows_group_by_ordinal = True 
    138138allows_unique_and_pk = True 
     139pk_requires_unique = False 
    139140autoindexes_primary_keys = False 
    140141needs_datetime_string_cast = True     # MySQLdb requires a typecast for dates 
     
    285286    output = [] 
    286287    output.append( 'ALTER TABLE '+ quote_name(table_name) +' DROP COLUMN '+ quote_name(col_name) + ';' ) 
    287     return '\n'.join(output) 
     288    return output 
    288289     
    289290     
  • django/branches/schema-evolution/django/db/backends/mysql_old/base.py

    r5734 r5785  
    152152allows_group_by_ordinal = True 
    153153allows_unique_and_pk = True 
     154pk_requires_unique = False 
    154155autoindexes_primary_keys = False 
    155156needs_datetime_string_cast = True     # MySQLdb requires a typecast for dates 
  • django/branches/schema-evolution/django/db/backends/postgresql/base.py

    r5784 r5785  
    118118allows_group_by_ordinal = True 
    119119allows_unique_and_pk = True 
     120pk_requires_unique = False 
    120121autoindexes_primary_keys = True 
    121122needs_datetime_string_cast = True 
     
    322323    output = [] 
    323324    output.append( 'ALTER TABLE '+ quote_name(table_name) +' DROP COLUMN '+ quote_name(col_name) + ';' ) 
    324     return '\n'.join(output) 
     325    return output 
    325326 
    326327# Register these custom typecasts, because Django expects dates/times to be 
  • django/branches/schema-evolution/django/db/backends/postgresql/introspection.py

    r5784 r5785  
    8282    dict['unique'] = False 
    8383    dict['default'] = '' 
    84              
    85 #    dict['allow_null'] = False 
     84    dict['allow_null'] = False 
     85 
    8686    for row in cursor.fetchall(): 
    8787        if row[0] == column_name: 
  • django/branches/schema-evolution/django/db/backends/postgresql_psycopg2/base.py

    r5734 r5785  
    8080allows_group_by_ordinal = True 
    8181allows_unique_and_pk = True 
     82pk_requires_unique = False 
    8283autoindexes_primary_keys = True 
    8384needs_datetime_string_cast = False 
  • django/branches/schema-evolution/django/db/backends/sqlite3/base.py

    r5784 r5785  
    33""" 
    44 
     5from django.core import management 
    56from django.db.backends import util 
    67try: 
     
    103104allows_group_by_ordinal = True 
    104105allows_unique_and_pk = True 
     106pk_requires_unique = True # or else the constraint is never created 
    105107autoindexes_primary_keys = True 
    106108needs_datetime_string_cast = True 
     
    228230    output.append( 'UPDATE '+ quote_name(table_name) +' SET '+ new_col_name +' = '+ old_col_name +' WHERE '+ pk_name +'=(select '+ pk_name +' from '+ table_name +');' ) 
    229231    output.append( '-- FYI: sqlite does not support deleting columns, so  '+ quote_name(old_col_name) +' remains as cruft' ) 
    230     # use the following when sqlite gets drop support 
    231     #output.append( 'ALTER TABLE '+ quote_name(table_name) +' DROP COLUMN '+ quote_name(old_col_name) ) 
    232232    return '\n'.join(output) 
    233233 
    234 def get_change_column_def_sql( table_name, col_name, col_def ): 
     234def get_change_column_def_sql( table_name, col_name, col_type, null, unique, primary_key ): 
    235235    # sqlite doesn't support column modifications, so we fake it 
    236236    output = [] 
     237    col_def = col_type +' '+ ('%sNULL' % (not null and 'NOT ' or '')) 
     238    if unique or primary_key: 
     239        col_def += ' '+ 'UNIQUE' 
     240    if primary_key: 
     241        col_def += ' '+ 'PRIMARY KEY' 
    237242    # TODO: fake via renaming the table, building a new one and deleting the old 
    238243    output.append('-- sqlite does not support column modifications '+ quote_name(table_name) +'.'+ quote_name(col_name) +' to '+ col_def) 
     
    248253    field_output.append(col_type) 
    249254    field_output.append(('%sNULL' % (not null and 'NOT ' or ''))) 
    250     if unique
     255    if unique or primary_key
    251256        field_output.append(('UNIQUE')) 
    252257    if primary_key: 
     
    256261 
    257262def get_drop_column_sql( table_name, col_name ): 
     263    model = get_model_from_table_name(table_name) 
    258264    output = [] 
    259     output.append( '-- FYI: sqlite does not support deleting columns, so  '+ quote_name(old_col_name) +' remains as cruft' ) 
    260     # use the following when sqlite gets drop support 
    261     # output.append( '-- ALTER TABLE '+ quote_name(table_name) +' DROP COLUMN '+ quote_name(col_name) ) 
    262     return '\n'.join(output) 
     265    output.append( '-- FYI: sqlite does not support deleting columns, so we create a new '+ quote_name(col_name) +' and delete the old  (ie, this could take a while)' ) 
     266    tmp_table_name = table_name + '_1337_TMP' # unlikely to produce a namespace conflict 
     267    output.append( get_change_table_name_sql( tmp_table_name, table_name ) ) 
     268    output.extend( management._get_sql_model_create(model, set())[0] ) 
     269    new_cols = [] 
     270    for f in model._meta.fields: 
     271        new_cols.append( quote_name(f.column) ) 
     272    output.append( 'INSERT INTO '+ quote_name(table_name) +' SELECT '+ ','.join(new_cols) +' FROM '+ quote_name(tmp_table_name) +';' ) 
     273    output.append( 'DROP TABLE '+ quote_name(tmp_table_name) +';' ) 
     274    return output 
     275 
     276def get_model_from_table_name(table_name): 
     277    from django.db import models 
     278    for app in models.get_apps(): 
     279        app_name = app.__name__.split('.')[-2] 
     280        if app_name == table_name.split('_')[0] or app_name == '_'.join(table_name.split('_')[0:1]) or app_name == '_'.join(table_name.split('_')[0:2]): 
     281            for model in models.get_models(app): 
     282                if model._meta.db_table == table_name: 
     283                    return model 
     284    return None 
    263285     
    264286 
  • django/branches/schema-evolution/django/db/backends/sqlite3/introspection.py

    r5735 r5785  
    5454    cursor.execute("PRAGMA table_info(%s)" % quote_name(table_name)) 
    5555    dict = {} 
     56    dict['primary_key'] = False 
     57    dict['foreign_key'] = False 
     58    dict['unique'] = False 
     59    dict['default'] = '' 
     60    dict['allow_null'] = True 
     61 
    5662    for row in cursor.fetchall(): 
     63#        print row 
    5764        if row[1] == column_name: 
     65            col_type = row[2] 
    5866 
    5967            # maxlength check goes here 
     
    6270             
    6371            # default flag check goes here 
    64             #if row[2]=='YES': dict['allow_null'] = True 
    65             #else: dict['allow_null'] = False 
     72            dict['allow_null'] = row[3]==0 
    6673             
     74            # default value check goes here 
     75            dict['default'] = row[4] 
     76 
     77    cursor.execute("PRAGMA index_list(%s)" % quote_name(table_name)) 
     78    index_names = [] 
     79    for row in cursor.fetchall(): 
     80        index_names.append(row[1]) 
     81    for index_name in index_names: 
     82        cursor.execute("PRAGMA index_info(%s)" % quote_name(index_name)) 
     83        for row in cursor.fetchall(): 
     84            if row[2]==column_name: 
     85                if col_type=='integer': dict['primary_key'] = True  # sqlite3 does not distinguish between unique and pk; all  
     86                else: dict['unique'] = True                         # unique integer columns are treated as part of the pk. 
     87 
    6788            # primary/foreign/unique key flag check goes here 
    6889            #if row[3]=='PRI': dict['primary_key'] = True 
     
    7394            #else: dict['unique'] = False 
    7495             
    75             # default value check goes here 
    76             # if row[4]=='NULL': dict['default'] = None 
    77             # else: dict['default'] = row[4] 
    78             #dict['default'] = row[4] 
    79              
    80     print table_name, column_name, dict 
     96 
     97#    print dict 
    8198    return dict 
    8299     
  • django/branches/schema-evolution/tests/modeltests/schema_evolution/models.py

    r5784 r5785  
    142142""" 
    143143 
     144if settings.DATABASE_ENGINE == 'sqlite3': 
     145    __test__['API_TESTS'] += """ 
     146# the table as it is supposed to be 
     147>>> create_table_sql = management.get_sql_all(app) 
     148>>> print create_table_sql 
     149['CREATE TABLE "schema_evolution_person" (\\n    "id" integer NOT NULL UNIQUE PRIMARY KEY,\\n    "name" varchar(20) NOT NULL,\\n    "gender" varchar(1) NOT NULL,\\n    "gender2" varchar(1) NOT NULL\\n)\\n;'] 
     150 
     151# make sure we don't evolve an unedited table 
     152>>> management.get_sql_evolution(app) 
     153[] 
     154 
     155# delete a column, so it looks like we've recently added a field 
     156>>> cursor.execute( 'DROP TABLE "schema_evolution_person";' ).__class__ 
     157<class 'django.db.backends.sqlite3.base.SQLiteCursorWrapper'> 
     158>>> cursor.execute( 'CREATE TABLE "schema_evolution_person" ( "id" integer NOT NULL UNIQUE PRIMARY KEY, "name" varchar(20) NOT NULL, "gender" varchar(1) NOT NULL );' ).__class__ 
     159<class 'django.db.backends.sqlite3.base.SQLiteCursorWrapper'> 
     160>>> management.get_sql_evolution(app) 
     161['ALTER TABLE "schema_evolution_person" ADD COLUMN "gender2" varchar(1) NOT NULL;'] 
     162 
     163# reset the db 
     164>>> cursor.execute('DROP TABLE schema_evolution_person;').__class__ 
     165<class 'django.db.backends.sqlite3.base.SQLiteCursorWrapper'> 
     166>>> cursor.execute(create_table_sql[0]).__class__ 
     167<class 'django.db.backends.sqlite3.base.SQLiteCursorWrapper'> 
     168 
     169# add a column, so it looks like we've recently deleted a field 
     170>>> cursor.execute( 'DROP TABLE "schema_evolution_person";' ).__class__ 
     171<class 'django.db.backends.sqlite3.base.SQLiteCursorWrapper'> 
     172>>> cursor.execute( 'CREATE TABLE "schema_evolution_person" ( "id" integer NOT NULL UNIQUE PRIMARY KEY, "name" varchar(20) NOT NULL, "gender" varchar(1) NOT NULL, "gender2" varchar(1) NOT NULL, "gender_new" varchar(1) NOT NULL );' ).__class__ 
     173<class 'django.db.backends.sqlite3.base.SQLiteCursorWrapper'> 
     174>>> management.get_sql_evolution(app) 
     175['-- warning: the following may cause data loss', u'-- FYI: sqlite does not support deleting columns, so we create a new "gender_new" and delete the old  (ie, this could take a while)', 'ALTER TABLE "schema_evolution_person" RENAME TO "schema_evolution_person_1337_TMP";', 'CREATE TABLE "schema_evolution_person" (\\n    "id" integer NOT NULL UNIQUE PRIMARY KEY,\\n    "name" varchar(20) NOT NULL,\\n    "gender" varchar(1) NOT NULL,\\n    "gender2" varchar(1) NOT NULL\\n)\\n;', 'INSERT INTO "schema_evolution_person" SELECT "id","name","gender","gender2" FROM "schema_evolution_person_1337_TMP";', 'DROP TABLE "schema_evolution_person_1337_TMP";', '-- end warning'] 
     176 
     177# reset the db 
     178>>> cursor.execute('DROP TABLE schema_evolution_person;').__class__ 
     179<class 'django.db.backends.sqlite3.base.SQLiteCursorWrapper'> 
     180>>> cursor.execute(create_table_sql[0]).__class__ 
     181<class 'django.db.backends.sqlite3.base.SQLiteCursorWrapper'> 
     182 
     183""" 
     184 
     185crap = """ 
     186 
     187# rename column, so it looks like we've recently renamed a field 
     188>>> cursor.execute( backend.get_change_column_name_sql( 'schema_evolution_person', {}, 'gender2', 'gender_old', 'varchar(1)' ) ) 
     189>>> management.get_sql_evolution(app) 
     190['ALTER TABLE "schema_evolution_person" RENAME COLUMN "gender_old" TO "gender2";'] 
     191 
     192# reset the db 
     193>>> cursor.execute('DROP TABLE schema_evolution_person;'); cursor.execute(create_table_sql[0]) 
     194 
     195# rename table, so it looks like we've recently renamed a model 
     196>>> cursor.execute( backend.get_change_table_name_sql( 'schema_evolution_personold', 'schema_evolution_person' ) ) 
     197>>> management.get_sql_evolution(app) 
     198['ALTER TABLE "schema_evolution_personold" RENAME TO "schema_evolution_person";'] 
     199 
     200# reset the db 
     201>>> cursor.execute(create_table_sql[0]) 
     202 
     203# change column flags, so it looks like we've recently changed a column flag 
     204>>> cursor.execute( backend.get_change_column_def_sql( 'schema_evolution_person', 'name', 'varchar(10)', True, False, False ) ) 
     205>>> management.get_sql_evolution(app) 
     206['ALTER TABLE "schema_evolution_person" ADD COLUMN "name_tmp" varchar(20);\\nUPDATE "schema_evolution_person" SET "name_tmp" = "name";\\nALTER TABLE "schema_evolution_person" DROP COLUMN "name";\\nALTER TABLE "schema_evolution_person" RENAME COLUMN "name_tmp" TO "name";\\nALTER TABLE "schema_evolution_person" ALTER COLUMN "name" SET NOT NULL;'] 
     207 
     208""" 
     209