| | 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 | |