Skip to content

Commit

Permalink
schema-evolution:
Browse files Browse the repository at this point in the history
moved most of the sql_evolve code into its own module

git-svn-id: http://code.djangoproject.com/svn/django/branches/schema-evolution@5789 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information
keredson committed Aug 3, 2007
1 parent 0f5a5a0 commit c05d52c
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 150 deletions.
158 changes: 8 additions & 150 deletions django/core/management.py
Expand Up @@ -481,9 +481,10 @@ def get_sql_indexes_for_model(model):
"%s;" % tablespace_sql
)
return output

def get_sql_evolution(app):
"Returns SQL to update an existing schema to match the existing models."
import schema_evolution
from django.db import get_creation_module, models, backend, get_introspection_module, connection
data_types = get_creation_module().DATA_TYPES

Expand Down Expand Up @@ -532,169 +533,26 @@ def get_sql_evolution(app):

for klass in app_models:

output, new_table_name = get_sql_evolution_check_for_changed_model_name(klass)
output, new_table_name = schema_evolution.get_sql_evolution_check_for_changed_model_name(klass)
final_output.extend(output)

output = get_sql_evolution_check_for_changed_field_flags(klass, new_table_name)
output = schema_evolution.get_sql_evolution_check_for_changed_field_flags(klass, new_table_name)
final_output.extend(output)

output = get_sql_evolution_check_for_changed_field_name(klass, new_table_name)
output = schema_evolution.get_sql_evolution_check_for_changed_field_name(klass, new_table_name)
final_output.extend(output)

output = get_sql_evolution_check_for_new_fields(klass, new_table_name)
output = schema_evolution.get_sql_evolution_check_for_new_fields(klass, new_table_name)
final_output.extend(output)

output = get_sql_evolution_check_for_dead_fields(klass, new_table_name)
output = schema_evolution.get_sql_evolution_check_for_dead_fields(klass, new_table_name)
final_output.extend(output)

return final_output

get_sql_evolution.help_doc = "Returns SQL to update an existing schema to match the existing models."
get_sql_evolution.args = APP_ARGS

def get_sql_evolution_check_for_new_fields(klass, new_table_name):
"checks for model fields that are not in the existing data structure"
from django.db import backend, get_creation_module, models, get_introspection_module, connection
data_types = get_creation_module().DATA_TYPES
cursor = connection.cursor()
introspection = get_introspection_module()
opts = klass._meta
output = []
db_table = klass._meta.db_table
if new_table_name:
db_table = new_table_name
for f in opts.fields:
existing_fields = introspection.get_columns(cursor,db_table)
if f.column not in existing_fields and (not f.aka or f.aka not in existing_fields and len(set(f.aka) & set(existing_fields))==0):
rel_field = f
data_type = f.get_internal_type()
col_type = data_types[data_type]
if col_type is not None:
output.extend( backend.get_add_column_sql( db_table, f.column, style.SQL_COLTYPE(col_type % rel_field.__dict__), f.null, f.unique, f.primary_key ) )
return output

def get_sql_evolution_check_for_changed_model_name(klass):
from django.db import backend, get_creation_module, models, get_introspection_module, connection
cursor = connection.cursor()
introspection = get_introspection_module()
table_list = introspection.get_table_list(cursor)
if klass._meta.db_table in table_list:
return [], None
if klass._meta.aka in table_list:
return backend.get_change_table_name_sql( klass._meta.db_table, klass._meta.aka), klass._meta.aka
elif len(set(klass._meta.aka) & set(table_list))==1:
return backend.get_change_table_name_sql( klass._meta.db_table, klass._meta.aka[0]), klass._meta.aka[0]
else:
return [], None

def get_sql_evolution_check_for_changed_field_name(klass, new_table_name):
from django.db import backend, get_creation_module, models, get_introspection_module, connection
data_types = get_creation_module().DATA_TYPES
cursor = connection.cursor()
introspection = get_introspection_module()
opts = klass._meta
output = []
db_table = klass._meta.db_table
if new_table_name:
db_table = new_table_name
for f in opts.fields:
existing_fields = introspection.get_columns(cursor,db_table)
if f.column not in existing_fields and f.aka and (f.aka in existing_fields or len(set(f.aka) & set(existing_fields)))==1:
old_col = None
if isinstance( f.aka, str ):
old_col = f.aka
else:
old_col = f.aka[0]
rel_field = f
data_type = f.get_internal_type()
col_type = data_types[data_type]
if col_type is not None:
col_def = style.SQL_COLTYPE(col_type % rel_field.__dict__) +' '+ style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or ''))
if f.unique:
col_def += style.SQL_KEYWORD(' UNIQUE')
if f.primary_key:
col_def += style.SQL_KEYWORD(' PRIMARY KEY')
output.extend( backend.get_change_column_name_sql( klass._meta.db_table, introspection.get_indexes(cursor,db_table), old_col, f.column, col_def ) )
return output

def get_sql_evolution_check_for_changed_field_flags(klass, new_table_name):
from django.db import backend, get_creation_module, models, get_introspection_module, connection
from django.db.models.fields import CharField, SlugField
from django.db.models.fields.related import RelatedField, ForeignKey
data_types = get_creation_module().DATA_TYPES
cursor = connection.cursor()
introspection = get_introspection_module()
opts = klass._meta
output = []
db_table = klass._meta.db_table
if new_table_name:
db_table = new_table_name
for f in opts.fields:
existing_fields = introspection.get_columns(cursor,db_table)
# print existing_fields
cf = None # current field, ie what it is before any renames
if f.column in existing_fields:
cf = f.column
elif f.aka in existing_fields:
cf = f.aka
elif f.aka and len(set(f.aka) & set(existing_fields))==1:
cf = f.aka[0]
else:
continue # no idea what column you're talking about - should be handled by get_sql_evolution_check_for_new_fields())
data_type = f.get_internal_type()
if data_types.has_key(data_type):
column_flags = introspection.get_known_column_flags(cursor, db_table, cf)
# print db_table, cf, column_flags
if column_flags['allow_null']!=f.null or \
( not f.primary_key and isinstance(f, CharField) and column_flags['maxlength']!=str(f.maxlength) ) or \
( not f.primary_key and isinstance(f, SlugField) and column_flags['maxlength']!=str(f.maxlength) ) or \
( column_flags['unique']!=f.unique and ( settings.DATABASE_ENGINE!='postgresql' or not f.primary_key ) ) or \
column_flags['primary_key']!=f.primary_key:
#column_flags['foreign_key']!=f.foreign_key:
# print 'need to change'
# print db_table, f.column, column_flags
# print "column_flags['allow_null']!=f.null", column_flags['allow_null']!=f.null
# print "not f.primary_key and isinstance(f, CharField) and column_flags['maxlength']!=str(f.maxlength)", not f.primary_key and isinstance(f, CharField) and column_flags['maxlength']!=str(f.maxlength)
# print "not f.primary_key and isinstance(f, SlugField) and column_flags['maxlength']!=str(f.maxlength)", not f.primary_key and isinstance(f, SlugField) and column_flags['maxlength']!=str(f.maxlength)
# print "column_flags['unique']!=f.unique", column_flags['unique']!=f.unique
# print "column_flags['primary_key']!=f.primary_key", column_flags['primary_key']!=f.primary_key
col_type = data_types[data_type]
col_type_def = style.SQL_COLTYPE(col_type % f.__dict__)
# col_def = style.SQL_COLTYPE(col_type % f.__dict__) +' '+ style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or ''))
# if f.unique:
# col_def += ' '+ style.SQL_KEYWORD('UNIQUE')
# if f.primary_key:
# col_def += ' '+ style.SQL_KEYWORD('PRIMARY KEY')
output.extend( backend.get_change_column_def_sql( db_table, cf, col_type_def, f.null, f.unique, f.primary_key ) )
#print db_table, cf, f.maxlength, introspection.get_known_column_flags(cursor, db_table, cf)
return output

def get_sql_evolution_check_for_dead_fields(klass, new_table_name):
from django.db import backend, get_creation_module, models, get_introspection_module, connection
from django.db.models.fields import CharField, SlugField
from django.db.models.fields.related import RelatedField, ForeignKey
data_types = get_creation_module().DATA_TYPES
cursor = connection.cursor()
introspection = get_introspection_module()
opts = klass._meta
output = []
db_table = klass._meta.db_table
if new_table_name:
db_table = new_table_name
suspect_fields = set(introspection.get_columns(cursor,db_table))
# print 'suspect_fields = ', suspect_fields
for f in opts.fields:
# print 'f = ', f
# print 'f.aka = ', f.aka
suspect_fields.discard(f.column)
suspect_fields.discard(f.aka)
if f.aka: suspect_fields.difference_update(f.aka)
if len(suspect_fields)>0:
output.append( '-- warning: the following may cause data loss' )
for suspect_field in suspect_fields:
output.extend( backend.get_drop_column_sql( db_table, suspect_field ) )
output.append( '-- end warning' )
return output

def get_sql_all(app):
"Returns a list of CREATE TABLE SQL, initial-data inserts, and CREATE INDEX SQL for the given module."
return get_sql_create(app) + get_custom_sql(app) + get_sql_indexes(app)
Expand Down
153 changes: 153 additions & 0 deletions django/core/schema_evolution.py
@@ -0,0 +1,153 @@
import django
from django.core.exceptions import ImproperlyConfigured
from optparse import OptionParser
from django.utils import termcolors
from django.conf import settings
import os, re, shutil, sys, textwrap
import management


def get_sql_evolution_check_for_new_fields(klass, new_table_name):
"checks for model fields that are not in the existing data structure"
from django.db import backend, get_creation_module, models, get_introspection_module, connection
data_types = get_creation_module().DATA_TYPES
cursor = connection.cursor()
introspection = get_introspection_module()
opts = klass._meta
output = []
db_table = klass._meta.db_table
if new_table_name:
db_table = new_table_name
for f in opts.fields:
existing_fields = introspection.get_columns(cursor,db_table)
if f.column not in existing_fields and (not f.aka or f.aka not in existing_fields and len(set(f.aka) & set(existing_fields))==0):
rel_field = f
data_type = f.get_internal_type()
col_type = data_types.get(data_type)
if col_type is not None:
output.extend( backend.get_add_column_sql( klass._meta.db_table, f.column, management.style.SQL_COLTYPE(col_type % rel_field.__dict__), f.null, f.unique, f.primary_key ) )
return output

def get_sql_evolution_check_for_changed_model_name(klass):
from django.db import backend, get_creation_module, models, get_introspection_module, connection
cursor = connection.cursor()
introspection = get_introspection_module()
table_list = introspection.get_table_list(cursor)
if klass._meta.db_table in table_list:
return [], None
if klass._meta.aka in table_list:
return backend.get_change_table_name_sql( klass._meta.db_table, klass._meta.aka), klass._meta.aka
elif len(set(klass._meta.aka) & set(table_list))==1:
return backend.get_change_table_name_sql( klass._meta.db_table, klass._meta.aka[0]), klass._meta.aka[0]
else:
return [], None

def get_sql_evolution_check_for_changed_field_name(klass, new_table_name):
from django.db import backend, get_creation_module, models, get_introspection_module, connection
data_types = get_creation_module().DATA_TYPES
cursor = connection.cursor()
introspection = get_introspection_module()
opts = klass._meta
output = []
db_table = klass._meta.db_table
if new_table_name:
db_table = new_table_name
for f in opts.fields:
existing_fields = introspection.get_columns(cursor,db_table)
if f.column not in existing_fields and f.aka and (f.aka in existing_fields or len(set(f.aka) & set(existing_fields)))==1:
old_col = None
if isinstance( f.aka, str ):
old_col = f.aka
else:
old_col = f.aka[0]
rel_field = f
data_type = f.get_internal_type()
col_type = data_types[data_type]
if col_type is not None:
col_def = management.style.SQL_COLTYPE(col_type % rel_field.__dict__) +' '+ management.style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or ''))
if f.unique:
col_def += management.style.SQL_KEYWORD(' UNIQUE')
if f.primary_key:
col_def += management.style.SQL_KEYWORD(' PRIMARY KEY')
output.extend( backend.get_change_column_name_sql( klass._meta.db_table, introspection.get_indexes(cursor,db_table), old_col, f.column, col_def ) )
return output

def get_sql_evolution_check_for_changed_field_flags(klass, new_table_name):
from django.db import backend, get_creation_module, models, get_introspection_module, connection
from django.db.models.fields import CharField, SlugField
from django.db.models.fields.related import RelatedField, ForeignKey
data_types = get_creation_module().DATA_TYPES
cursor = connection.cursor()
introspection = get_introspection_module()
opts = klass._meta
output = []
db_table = klass._meta.db_table
if new_table_name:
db_table = new_table_name
for f in opts.fields:
existing_fields = introspection.get_columns(cursor,db_table)
# print existing_fields
cf = None # current field, ie what it is before any renames
if f.column in existing_fields:
cf = f.column
elif f.aka in existing_fields:
cf = f.aka
elif f.aka and len(set(f.aka) & set(existing_fields))==1:
cf = f.aka[0]
else:
continue # no idea what column you're talking about - should be handled by get_sql_evolution_check_for_new_fields())
data_type = f.get_internal_type()
if data_types.has_key(data_type):
column_flags = introspection.get_known_column_flags(cursor, db_table, cf)
# print db_table, cf, column_flags
if column_flags['allow_null']!=f.null or \
( not f.primary_key and isinstance(f, CharField) and column_flags['maxlength']!=str(f.maxlength) ) or \
( not f.primary_key and isinstance(f, SlugField) and column_flags['maxlength']!=str(f.maxlength) ) or \
( column_flags['unique']!=f.unique and ( settings.DATABASE_ENGINE!='postgresql' or not f.primary_key ) ) or \
column_flags['primary_key']!=f.primary_key:
#column_flags['foreign_key']!=f.foreign_key:
# print 'need to change'
# print db_table, f.column, column_flags
# print "column_flags['allow_null']!=f.null", column_flags['allow_null']!=f.null
# print "not f.primary_key and isinstance(f, CharField) and column_flags['maxlength']!=str(f.maxlength)", not f.primary_key and isinstance(f, CharField) and column_flags['maxlength']!=str(f.maxlength)
# print "not f.primary_key and isinstance(f, SlugField) and column_flags['maxlength']!=str(f.maxlength)", not f.primary_key and isinstance(f, SlugField) and column_flags['maxlength']!=str(f.maxlength)
# print "column_flags['unique']!=f.unique", column_flags['unique']!=f.unique
# print "column_flags['primary_key']!=f.primary_key", column_flags['primary_key']!=f.primary_key
col_type = data_types[data_type]
col_type_def = management.style.SQL_COLTYPE(col_type % f.__dict__)
# col_def = style.SQL_COLTYPE(col_type % f.__dict__) +' '+ style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or ''))
# if f.unique:
# col_def += ' '+ style.SQL_KEYWORD('UNIQUE')
# if f.primary_key:
# col_def += ' '+ style.SQL_KEYWORD('PRIMARY KEY')
output.extend( backend.get_change_column_def_sql( klass._meta.db_table, cf, col_type_def, f.null, f.unique, f.primary_key ) )
#print db_table, cf, f.maxlength, introspection.get_known_column_flags(cursor, db_table, cf)
return output

def get_sql_evolution_check_for_dead_fields(klass, new_table_name):
from django.db import backend, get_creation_module, models, get_introspection_module, connection
from django.db.models.fields import CharField, SlugField
from django.db.models.fields.related import RelatedField, ForeignKey
data_types = get_creation_module().DATA_TYPES
cursor = connection.cursor()
introspection = get_introspection_module()
opts = klass._meta
output = []
db_table = klass._meta.db_table
if new_table_name:
db_table = new_table_name
suspect_fields = set(introspection.get_columns(cursor,db_table))
# print 'suspect_fields = ', suspect_fields
for f in opts.fields:
# print 'f = ', f
# print 'f.aka = ', f.aka
suspect_fields.discard(f.column)
suspect_fields.discard(f.aka)
if f.aka: suspect_fields.difference_update(f.aka)
if len(suspect_fields)>0:
output.append( '-- warning: the following may cause data loss' )
for suspect_field in suspect_fields:
output.extend( backend.get_drop_column_sql( klass._meta.db_table, suspect_field ) )
output.append( '-- end warning' )
return output

0 comments on commit c05d52c

Please sign in to comment.