mirror of
https://github.com/django/django.git
synced 2025-07-05 10:19:20 +00:00
schema-evolution:
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
This commit is contained in:
parent
0f5a5a0594
commit
c05d52cdfe
@ -481,9 +481,10 @@ def get_sql_indexes_for_model(model):
|
|||||||
"%s;" % tablespace_sql
|
"%s;" % tablespace_sql
|
||||||
)
|
)
|
||||||
return output
|
return output
|
||||||
|
|
||||||
def get_sql_evolution(app):
|
def get_sql_evolution(app):
|
||||||
"Returns SQL to update an existing schema to match the existing models."
|
"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
|
from django.db import get_creation_module, models, backend, get_introspection_module, connection
|
||||||
data_types = get_creation_module().DATA_TYPES
|
data_types = get_creation_module().DATA_TYPES
|
||||||
|
|
||||||
@ -532,169 +533,26 @@ def get_sql_evolution(app):
|
|||||||
|
|
||||||
for klass in app_models:
|
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)
|
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)
|
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)
|
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)
|
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)
|
final_output.extend(output)
|
||||||
|
|
||||||
return final_output
|
return final_output
|
||||||
|
|
||||||
get_sql_evolution.help_doc = "Returns SQL to update an existing schema to match the existing models."
|
get_sql_evolution.help_doc = "Returns SQL to update an existing schema to match the existing models."
|
||||||
get_sql_evolution.args = APP_ARGS
|
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):
|
def get_sql_all(app):
|
||||||
"Returns a list of CREATE TABLE SQL, initial-data inserts, and CREATE INDEX SQL for the given module."
|
"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)
|
return get_sql_create(app) + get_custom_sql(app) + get_sql_indexes(app)
|
||||||
|
153
django/core/schema_evolution.py
Normal file
153
django/core/schema_evolution.py
Normal file
@ -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
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user