summaryrefslogtreecommitdiff
path: root/django/core/schema_evolution.py
blob: 76660827d31dbba3e2968b6475e87c569b07c90b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
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