| 131 | #------------------------------------------------------------------------------ |
| 132 | |
| 133 | def table_field_exists(klass, f): |
| 134 | """ |
| 135 | Checks if a field is available within a table |
| 136 | |
| 137 | Limited implementation, verifies only field-name |
| 138 | |
| 139 | Status: draft |
| 140 | """ |
| 141 | from django.db import backend, connection |
| 142 | |
| 143 | cursor = connection.cursor() |
| 144 | backend_column_name = f.column |
| 145 | |
| 146 | cursor.execute('select * from %s where 1 = 0' % klass._meta.db_table ) |
| 147 | |
| 148 | exist = False |
| 149 | for col in cursor.description: |
| 150 | if backend_column_name == col[0]: |
| 151 | exist = True |
| 152 | break |
| 153 | |
| 154 | return exist |
| 155 | |
| 156 | #------------------------------------------------------------------------------ |
| 157 | def _get_sql_table_evolve(klass): |
| 158 | """ |
| 159 | Get the SQL required to evolve a single model. |
| 160 | |
| 161 | Uses ALTER TABLE |
| 162 | |
| 163 | Status: draft, sqlite3 specific. |
| 164 | """ |
| 165 | from django.db import backend |
| 166 | |
| 167 | final_output = [] |
| 168 | str = '' |
| 169 | for f in klass._meta.fields: |
| 170 | if not table_field_exists(klass, f): |
| 171 | statement, ref = _get_sql_field_def(klass, f) |
| 172 | field_def = ' '.join(statement) |
| 173 | |
| 174 | full_statement = [style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD COLUMN %s;' % \ |
| 175 | ( style.SQL_TABLE(backend.quote_name( klass._meta.db_table )), field_def) ] |
| 176 | final_output.append('\n'.join(full_statement)) |
| 177 | |
| 178 | if final_output: |
| 179 | full_statement = [style.SQL_KEYWORD('VACUUM') + ';'] |
| 180 | final_output.append('\n'.join(full_statement)) |
| 181 | |
| 182 | return final_output |
| 183 | |
| 184 | #------------------------------------------------------------------------------ |
| 185 | |
| 186 | def table_evolve(klass): |
| 187 | """ |
| 188 | evolves the underlying table for a single model |
| 189 | |
| 190 | Status: draft |
| 191 | """ |
| 192 | from django.db import backend, connection, transaction |
| 193 | |
| 194 | print "Checking table %s model" % klass.__name__ |
| 195 | |
| 196 | cursor = connection.cursor() |
| 197 | statements = _get_sql_table_evolve(klass) |
| 198 | |
| 199 | if statements: |
| 200 | print "Evolving table for Class %s" % klass.__name__ |
| 201 | for statement in statements: |
| 202 | cursor.execute(statement) |
| 203 | |
| 204 | transaction.commit_unless_managed() |
| 205 | |
| 206 | #------------------------------------------------------------------------------ |
| 207 | |
| 208 | def _get_sql_field_def(klass, f, models_already_seen=set() ): |
| 209 | """ |
| 210 | Get the SQL for a field definition |
| 211 | """ |
| 212 | |
| 213 | # copied from _get_sql_model_create |
| 214 | # not neede lines are outcommented (will be removed after review) |
| 215 | |
| 216 | from django.db import backend, get_creation_module, models |
| 217 | data_types = get_creation_module().DATA_TYPES |
| 218 | |
| 219 | #opts = klass._meta |
| 220 | #final_output = [] |
| 221 | #table_output = [] |
| 222 | #pending_references = {} |
| 223 | |
| 224 | if isinstance(f, models.ForeignKey): |
| 225 | rel_field = f.rel.get_related_field() |
| 226 | data_type = get_rel_data_type(rel_field) |
| 227 | else: |
| 228 | rel_field = f |
| 229 | data_type = f.get_internal_type() |
| 230 | col_type = data_types[data_type] |
| 231 | if col_type is not None: |
| 232 | # Make the definition (e.g. 'foo VARCHAR(30)') for this field. |
| 233 | field_output = [style.SQL_FIELD(backend.quote_name(f.column)), |
| 234 | style.SQL_COLTYPE(col_type % rel_field.__dict__)] |
| 235 | field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or ''))) |
| 236 | if f.unique: |
| 237 | field_output.append(style.SQL_KEYWORD('UNIQUE')) |
| 238 | if f.primary_key: |
| 239 | field_output.append(style.SQL_KEYWORD('PRIMARY KEY')) |
| 240 | if f.rel: |
| 241 | if f.rel.to in models_already_seen: |
| 242 | field_output.append(style.SQL_KEYWORD('REFERENCES') + ' ' + \ |
| 243 | style.SQL_TABLE(backend.quote_name(f.rel.to._meta.db_table)) + ' (' + \ |
| 244 | style.SQL_FIELD(backend.quote_name(f.rel.to._meta.get_field(f.rel.field_name).column)) + ')' |
| 245 | ) |
| 246 | #else: |
| 247 | # We haven't yet created the table to which this field |
| 248 | # is related, so save it for later. |
| 249 | #pr = pending_references.setdefault(f.rel.to, []).append((klass, f)) |
| 250 | #table_output.append(' '.join(field_output)) |
| 251 | |
| 252 | # code added during refactoring |
| 253 | has_pending_ref = False |
| 254 | if f.rel: |
| 255 | if f.rel.to not in models_already_seen: |
| 256 | has_pending_ref = True |
| 257 | |
| 258 | |
| 259 | return field_output, has_pending_ref |
| 260 | |
| 261 | #------------------------------------------------------------------------------ |
| 262 | |
144 | | for f in opts.fields: |
145 | | if isinstance(f, models.ForeignKey): |
146 | | rel_field = f.rel.get_related_field() |
147 | | data_type = get_rel_data_type(rel_field) |
148 | | else: |
149 | | rel_field = f |
150 | | data_type = f.get_internal_type() |
151 | | col_type = data_types[data_type] |
152 | | if col_type is not None: |
153 | | # Make the definition (e.g. 'foo VARCHAR(30)') for this field. |
154 | | field_output = [style.SQL_FIELD(backend.quote_name(f.column)), |
155 | | style.SQL_COLTYPE(col_type % rel_field.__dict__)] |
156 | | field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or ''))) |
157 | | if f.unique: |
158 | | field_output.append(style.SQL_KEYWORD('UNIQUE')) |
159 | | if f.primary_key: |
160 | | field_output.append(style.SQL_KEYWORD('PRIMARY KEY')) |
161 | | if f.rel: |
162 | | if f.rel.to in models_already_seen: |
163 | | field_output.append(style.SQL_KEYWORD('REFERENCES') + ' ' + \ |
164 | | style.SQL_TABLE(backend.quote_name(f.rel.to._meta.db_table)) + ' (' + \ |
165 | | style.SQL_FIELD(backend.quote_name(f.rel.to._meta.get_field(f.rel.field_name).column)) + ')' |
166 | | ) |
167 | | else: |
168 | | # We haven't yet created the table to which this field |
169 | | # is related, so save it for later. |
170 | | pr = pending_references.setdefault(f.rel.to, []).append((klass, f)) |
171 | | table_output.append(' '.join(field_output)) |
| 276 | |
| 277 | for f in opts.fields: |
| 278 | field_output, has_pending_ref = _get_sql_field_def(klass, f, models_already_seen) |
| 279 | table_output.append(' '.join(field_output)) |
| 280 | |
| 281 | if has_pending_ref: |
| 282 | pr = pending_references.setdefault(f.rel.to, []).append((klass, f)) |
| 283 | |