Changeset 3646
- Timestamp:
- 08/22/06 11:16:38 (2 years ago)
- Files:
-
- django/branches/schema-evolution/django/core/management.py (modified) (33 diffs)
- django/branches/schema-evolution/django/db/backends/mysql/base.py (modified) (2 diffs)
- django/branches/schema-evolution/django/db/backends/mysql/introspection.py (modified) (2 diffs)
- django/branches/schema-evolution/django/db/backends/postgresql/base.py (modified) (1 diff)
- django/branches/schema-evolution/django/db/backends/postgresql/introspection.py (modified) (2 diffs)
- django/branches/schema-evolution/django/db/backends/sqlite3/base.py (modified) (1 diff)
- django/branches/schema-evolution/django/db/backends/sqlite3/introspection.py (modified) (2 diffs)
- django/branches/schema-evolution/django/db/models/fields/__init__.py (modified) (7 diffs)
- django/branches/schema-evolution/django/db/models/options.py (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/branches/schema-evolution/django/core/management.py
r3307 r3646 46 46 style = dummy() 47 47 48 # Disable terminal coloring on Windows or if somebody's piping the output.49 if sys.platform == 'win32' or not sys.stdout.isatty():48 # Disable terminal coloring on Windows, Pocket PC, or if somebody's piping the output. 49 if sys.platform == 'win32' or sys.platform == 'Pocket PC' or not sys.stdout.isatty(): 50 50 disable_termcolors() 51 51 … … 79 79 v = '.'.join([str(i) for i in VERSION[:-1]]) 80 80 if VERSION[-1]: 81 v += ' (%s)' %VERSION[-1]81 v += '-' + VERSION[-1] 82 82 return v 83 83 … … 95 95 sys.exit(1) 96 96 97 # Get installed models, so we generate REFERENCES right 98 installed_models = _get_installed_models(_get_table_list()) 99 97 # Get installed models, so we generate REFERENCES right. 98 # We trim models from the current app so that the sqlreset command does not 99 # generate invalid SQL (leaving models out of known_models is harmless, so 100 # we can be conservative). 101 app_models = models.get_models(app) 100 102 final_output = [] 101 models_output = set(installed_models)103 known_models = set([model for model in _get_installed_models(_get_table_list()) if model not in app_models]) 102 104 pending_references = {} 103 105 104 app_models = models.get_models(app) 105 106 for klass in app_models: 107 output, references = _get_sql_model_create(klass, models_output) 106 107 for model in app_models: 108 output, references = _get_sql_model_create(model, known_models) 108 109 final_output.extend(output) 109 110 for refto, refs in references.items(): 110 try: 111 pending_references[refto].extend(refs) 112 except KeyError: 113 pending_references[refto] = refs 114 final_output.extend(_get_sql_for_pending_references(klass, pending_references)) 111 pending_references.setdefault(refto,[]).extend(refs) 112 final_output.extend(_get_sql_for_pending_references(model, pending_references)) 115 113 # Keep track of the fact that we've created the table for this model. 116 models_output.add(klass)114 known_models.add(model) 117 115 118 116 # Create the many-to-many join tables. 119 for klassin app_models:120 final_output.extend(_get_many_to_many_sql_for_model( klass))117 for model in app_models: 118 final_output.extend(_get_many_to_many_sql_for_model(model)) 121 119 122 120 # Handle references to tables that are from other apps … … 124 122 not_installed_models = set(pending_references.keys()) 125 123 if not_installed_models: 126 final_output.append('-- The following references should be added but depend on non-existant tables:') 127 for klass in not_installed_models: 128 final_output.extend(['-- ' + sql for sql in 129 _get_sql_for_pending_references(klass, pending_references)]) 124 alter_sql = [] 125 for model in not_installed_models: 126 alter_sql.extend(['-- ' + sql for sql in 127 _get_sql_for_pending_references(model, pending_references)]) 128 if alter_sql: 129 final_output.append('-- The following references should be added but depend on non-existent tables:') 130 final_output.extend(alter_sql) 130 131 131 132 return final_output … … 133 134 get_sql_create.args = APP_ARGS 134 135 135 def _get_sql_model_create( klass, models_already_seen=set()):136 def _get_sql_model_create(model, known_models=set()): 136 137 """ 137 138 Get the SQL required to create a single model. … … 142 143 data_types = get_creation_module().DATA_TYPES 143 144 144 opts = klass._meta145 opts = model._meta 145 146 final_output = [] 146 147 table_output = [] … … 164 165 field_output.append(style.SQL_KEYWORD('PRIMARY KEY')) 165 166 if f.rel: 166 if f.rel.to in models_already_seen:167 if f.rel.to in known_models: 167 168 field_output.append(style.SQL_KEYWORD('REFERENCES') + ' ' + \ 168 169 style.SQL_TABLE(backend.quote_name(f.rel.to._meta.db_table)) + ' (' + \ … … 172 173 # We haven't yet created the table to which this field 173 174 # is related, so save it for later. 174 pr = pending_references.setdefault(f.rel.to, []).append(( klass, f))175 pr = pending_references.setdefault(f.rel.to, []).append((model, f)) 175 176 table_output.append(' '.join(field_output)) 176 177 if opts.order_with_respect_to: … … 190 191 return final_output, pending_references 191 192 192 def _get_sql_for_pending_references( klass, pending_references):193 def _get_sql_for_pending_references(model, pending_references): 193 194 """ 194 195 Get any ALTER TABLE statements to add constraints after the fact. … … 199 200 final_output = [] 200 201 if backend.supports_constraints: 201 opts = klass._meta202 if klassin pending_references:203 for rel_class, f in pending_references[ klass]:202 opts = model._meta 203 if model in pending_references: 204 for rel_class, f in pending_references[model]: 204 205 rel_opts = rel_class._meta 205 206 r_table = rel_opts.db_table … … 207 208 table = opts.db_table 208 209 col = opts.get_field(f.rel.field_name).column 210 # For MySQL, r_name must be unique in the first 64 characters. 211 # So we are careful with character usage here. 212 r_name = '%s_refs_%s_%x' % (r_col, col, abs(hash((r_table, table)))) 209 213 final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s);' % \ 210 (backend.quote_name(r_table), 211 backend.quote_name('%s_referencing_%s_%s' % (r_col, table, col)), 214 (backend.quote_name(r_table), r_name, 212 215 backend.quote_name(r_col), backend.quote_name(table), backend.quote_name(col))) 213 del pending_references[ klass]216 del pending_references[model] 214 217 return final_output 215 218 216 def _get_many_to_many_sql_for_model( klass):219 def _get_many_to_many_sql_for_model(model): 217 220 from django.db import backend, get_creation_module 218 221 from django.db.models import GenericRel 219 222 220 223 data_types = get_creation_module().DATA_TYPES 221 224 222 opts = klass._meta225 opts = model._meta 223 226 final_output = [] 224 227 for f in opts.many_to_many: … … 274 277 references_to_delete = {} 275 278 app_models = models.get_models(app) 276 for klassin app_models:277 if cursor and klass._meta.db_table in table_names:279 for model in app_models: 280 if cursor and model._meta.db_table in table_names: 278 281 # The table exists, so it needs to be dropped 279 opts = klass._meta282 opts = model._meta 280 283 for f in opts.fields: 281 284 if f.rel and f.rel.to not in to_delete: 282 references_to_delete.setdefault(f.rel.to, []).append( ( klass, f) )283 284 to_delete.add( klass)285 286 for klassin app_models:287 if cursor and klass._meta.db_table in table_names:285 references_to_delete.setdefault(f.rel.to, []).append( (model, f) ) 286 287 to_delete.add(model) 288 289 for model in app_models: 290 if cursor and model._meta.db_table in table_names: 288 291 # Drop the table now 289 292 output.append('%s %s;' % (style.SQL_KEYWORD('DROP TABLE'), 290 style.SQL_TABLE(backend.quote_name( klass._meta.db_table))))291 if backend.supports_constraints and references_to_delete.has_key( klass):292 for rel_class, f in references_to_delete[ klass]:293 style.SQL_TABLE(backend.quote_name(model._meta.db_table)))) 294 if backend.supports_constraints and references_to_delete.has_key(model): 295 for rel_class, f in references_to_delete[model]: 293 296 table = rel_class._meta.db_table 294 297 col = f.column 295 r_table = klass._meta.db_table296 r_col = klass._meta.get_field(f.rel.field_name).column298 r_table = model._meta.db_table 299 r_col = model._meta.get_field(f.rel.field_name).column 297 300 output.append('%s %s %s %s;' % \ 298 301 (style.SQL_KEYWORD('ALTER TABLE'), 299 302 style.SQL_TABLE(backend.quote_name(table)), 300 303 style.SQL_KEYWORD(backend.get_drop_foreignkey_sql()), 301 style.SQL_FIELD(backend.quote_name( "%s_referencing_%s_%s" % (col, r_table, r_col)))))302 del references_to_delete[ klass]304 style.SQL_FIELD(backend.quote_name('%s_refs_%s_%x' % (col, r_col, abs(hash((table, r_table)))))))) 305 del references_to_delete[model] 303 306 304 307 # Output DROP TABLE statements for many-to-many tables. 305 for klassin app_models:306 opts = klass._meta308 for model in app_models: 309 opts = model._meta 307 310 for f in opts.many_to_many: 308 311 if cursor and f.m2m_db_table() in table_names: … … 361 364 app_dir = os.path.normpath(os.path.join(os.path.dirname(app.__file__), 'sql')) 362 365 363 for klassin app_models:364 output.extend(get_sql_initial_data_for_model( klass))366 for model in app_models: 367 output.extend(get_sql_initial_data_for_model(model)) 365 368 366 369 return output … … 372 375 from django.db import backend, models 373 376 output = [] 374 for klassin models.get_models(app):375 for f in klass._meta.fields:377 for model in models.get_models(app): 378 for f in model._meta.fields: 376 379 if isinstance(f, models.AutoField): 377 380 output.append("%s setval('%s', (%s max(%s) %s %s));" % \ 378 381 (style.SQL_KEYWORD('SELECT'), 379 style.SQL_FIELD('%s_%s_seq' % ( klass._meta.db_table, f.column)),382 style.SQL_FIELD('%s_%s_seq' % (model._meta.db_table, f.column)), 380 383 style.SQL_KEYWORD('SELECT'), 381 384 style.SQL_FIELD(backend.quote_name(f.column)), 382 385 style.SQL_KEYWORD('FROM'), 383 style.SQL_TABLE(backend.quote_name( klass._meta.db_table))))386 style.SQL_TABLE(backend.quote_name(model._meta.db_table)))) 384 387 break # Only one AutoField is allowed per model, so don't bother continuing. 385 for f in klass._meta.many_to_many:388 for f in model._meta.many_to_many: 386 389 output.append("%s setval('%s', (%s max(%s) %s %s));" % \ 387 390 (style.SQL_KEYWORD('SELECT'), … … 400 403 output = [] 401 404 402 for klassin models.get_models(app):403 for f in klass._meta.fields:405 for model in models.get_models(app): 406 for f in model._meta.fields: 404 407 if f.db_index: 405 408 unique = f.unique and 'UNIQUE ' or '' 406 409 output.append( 407 410 style.SQL_KEYWORD('CREATE %sINDEX' % unique) + ' ' + \ 408 style.SQL_TABLE('%s_%s' % ( klass._meta.db_table, f.column)) + ' ' + \411 style.SQL_TABLE('%s_%s' % (model._meta.db_table, f.column)) + ' ' + \ 409 412 style.SQL_KEYWORD('ON') + ' ' + \ 410 style.SQL_TABLE(backend.quote_name( klass._meta.db_table)) + ' ' + \413 style.SQL_TABLE(backend.quote_name(model._meta.db_table)) + ' ' + \ 411 414 "(%s);" % style.SQL_FIELD(backend.quote_name(f.column)) 412 415 ) … … 414 417 get_sql_indexes.help_doc = "Prints the CREATE INDEX SQL statements for the given model module name(s)." 415 418 get_sql_indexes.args = APP_ARGS 419 420 def get_sql_evolution(app): 421 "Returns SQL to update an existing schema to match the existing models." 422 from django.db import get_creation_module, models, backend, get_introspection_module, connection 423 data_types = get_creation_module().DATA_TYPES 424 425 if not data_types: 426 # This must be the "dummy" database backend, which means the user 427 # hasn't set DATABASE_ENGINE. 428 sys.stderr.write(style.ERROR("Error: Django doesn't know which syntax to use for your SQL statements,\n" + 429 "because you haven't specified the DATABASE_ENGINE setting.\n" + 430 "Edit your settings file and change DATABASE_ENGINE to something like 'postgresql' or 'mysql'.\n")) 431 sys.exit(1) 432 433 # First, try validating the models. 434 _check_for_validation_errors() 435 436 final_output = [] 437 438 # stolen and trimmed from syncdb so that we know which models are about 439 # to be created (so we don't check them for updates) 440 table_list = _get_table_list() 441 seen_models = _get_installed_models(table_list) 442 created_models = set() 443 pending_references = {} 444 445 model_list = models.get_models(app) 446 for model in model_list: 447 # Create the model's database table, if it doesn't already exist. 448 if model._meta.db_table in table_list or model._meta.aka in table_list or len(set(model._meta.aka) & set(table_list))>0: 449 continue 450 sql, references = _get_sql_model_create(model, seen_models) 451 seen_models.add(model) 452 created_models.add(model) 453 table_list.append(model._meta.db_table) 454 455 introspection = get_introspection_module() 456 # This should work even if a connecton isn't available 457 try: 458 cursor = connection.cursor() 459 except: 460 cursor = None 461 462 # get the existing models, minus the models we've just created 463 app_models = models.get_models(app) 464 for model in created_models: 465 if model in app_models: 466 app_models.remove(model) 467 468 for klass in app_models: 469 470 output, new_table_name = get_sql_evolution_check_for_changed_model_name(klass) 471 final_output.extend(output) 472 473 output = get_sql_evolution_check_for_changed_field_flags(klass, new_table_name) 474 final_output.extend(output) 475 476 output = get_sql_evolution_check_for_changed_field_name(klass, new_table_name) 477 final_output.extend(output) 478 479 output = get_sql_evolution_check_for_new_fields(klass, new_table_name) 480 final_output.extend(output) 481 482 output = get_sql_evolution_check_for_dead_fields(klass, new_table_name) 483 final_output.extend(output) 484 485 return final_output 486 get_sql_evolution.help_doc = "Returns SQL to update an existing schema to match the existing models." 487 get_sql_evolution.args = APP_ARGS 488 489 def get_sql_evolution_check_for_new_fields(klass, new_table_name): 490 "checks for model fields that are not in the existing data structure" 491 from django.db import backend, get_creation_module, models, get_introspection_module, connection 492 data_types = get_creation_module().DATA_TYPES 493 cursor = connection.cursor() 494 introspection = get_introspection_module() 495 opts = klass._meta 496 output = [] 497 db_table = klass._meta.db_table 498 if new_table_name: 499 db_table = new_table_name 500 for f in opts.fields: 501 existing_fields = introspection.get_columns(cursor,db_table) 502 if f.column not in existing_fields and f.aka not in existing_fields and len(set(f.aka) & set(existing_fields))==0: 503 rel_field = f 504 data_type = f.get_internal_type() 505 col_type = data_types[data_type] 506 if col_type is not None: 507 # field_output = [] 508 # field_output.append('ALTER TABLE') 509 # field_output.append(db_table) 510 # field_output.append('ADD COLUMN') 511 # field_output.append(backend.quote_name(f.column)) 512 # field_output.append(style.SQL_COLTYPE(col_type % rel_field.__dict__)) 513 # field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or ''))) 514 # if f.unique: 515 # field_output.append(style.SQL_KEYWORD('UNIQUE')) 516 # if f.primary_key: 517 # field_output.append(style.SQL_KEYWORD('PRIMARY KEY')) 518 # output.append(' '.join(field_output) + ';') 519 output.append( backend.get_add_column_sql( db_table, f.column, style.SQL_COLTYPE(col_type % rel_field.__dict__), f.null, f.unique, f.primary_key ) ) 520 return output 521 522 def get_sql_evolution_check_for_changed_model_name(klass): 523 from django.db import backend, get_creation_module, models, get_introspection_module, connection 524 cursor = connection.cursor() 525 introspection = get_introspection_module() 526 table_list = introspection.get_table_list(cursor) 527 if klass._meta.db_table in table_list: 528 return [], None 529 if klass._meta.aka in table_list: 530 return [ 'ALTER TABLE '+ backend.quote_name(klass._meta.aka) +' RENAME TO '+ backend.quote_name(klass._meta.db_table) + ';' ], klass._meta.aka 531 elif len(set(klass._meta.aka) & set(table_list))==1: 532 return [ 'ALTER TABLE '+ backend.quote_name(klass._meta.aka[0]) +' RENAME TO '+ backend.quote_name(klass._meta.db_table) + ';' ], klass._meta.aka[0] 533 else: 534 return [], None 535 536 def get_sql_evolution_check_for_changed_field_name(klass, new_table_name): 537 from django.db import backend, get_creation_module, models, get_introspection_module, connection 538 data_types = get_creation_module().DATA_TYPES 539 cursor = connection.cursor() 540 introspection = get_introspection_module() 541 opts = klass._meta 542 output = [] 543 db_table = klass._meta.db_table 544 if new_table_name: 545 db_table = new_table_name 546 for f in opts.fields: 547 existing_fields = introspection.get_columns(cursor,db_table) 548 if f.column not in existing_fields and (f.aka in existing_fields or len(set(f.aka) & set(existing_fields)))==1: 549 old_col = None 550 if isinstance( f.aka, str ): 551 old_col = f.aka 552 else: 553 old_col = f.aka[0] 554 rel_field = f 555 data_type = f.get_internal_type() 556 col_type = data_types[data_type] 557 if col_type is not None: 558 field_output = [] 559 col_def = style.SQL_COLTYPE(col_type % rel_field.__dict__) +' '+ style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or '')) 560 if f.unique: 561 col_def += style.SQL_KEYWORD(' UNIQUE') 562 if f.primary_key: 563 col_def += style.SQL_KEYWORD(' PRIMARY KEY') 564 field_output.append( backend.get_change_column_name_sql( klass._meta.db_table, introspection.get_indexes(cursor,db_table), backend.quote_name(old_col), backend.quote_name(f.column), col_def ) ) 565 output.append(' '.join(field_output)) 566 return output 567 568 def get_sql_evolution_check_for_changed_field_flags(klass, new_table_name): 569 from django.db import backend, get_creation_module, models, get_introspection_module, connection 570 from django.db.models.fields import CharField, SlugField 571 from django.db.models.fields.related import RelatedField, ForeignKey 572 data_types = get_creation_module().DATA_TYPES 573 cursor = connection.cursor() 574 introspection = get_introspection_module() 575 opts = klass._meta 576 output = [] 577 db_table = klass._meta.db_table 578 if new_table_name: 579 db_table = new_table_name 580 for f in opts.fields: 581 existing_fields = introspection.get_columns(cursor,db_table) 582 cf = None # current field, ie what it is before any renames 583 if f.column in existing_fields: 584 cf = f.column 585 elif f.aka in existing_fields: 586 cf = f.aka 587 elif len(set(f.aka) & set(existing_fields))==1: 588 cf = f.aka[0] 589 else: 590 continue # no idea what column you're talking about - should be handled by get_sql_evolution_check_for_new_fields()) 591 data_type = f.get_internal_type() 592 if data_types.has_key(data_type): 593 column_flags = introspection.get_known_column_flags(cursor, db_table, cf) 594 if column_flags['allow_null']!=f.null or \ 595 ( not f.primary_key and isinstance(f, CharField) and column_flags['maxlength']!=str(f.maxlength) ) or \ 596 ( not f.primary_key and isinstance(f, SlugField) and column_flags['maxlength']!=str(f.maxlength) ) or \ 597 column_flags['unique']!=f.unique or \ 598 column_flags['primary_key']!=f.primary_key: 599 #column_flags['foreign_key']!=f.foreign_key: 600 # print 601 # print db_table, f.column, column_flags 602 # print "column_flags['allow_null']!=f.null", column_flags['allow_null']!=f.null 603 # 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) 604 # 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) 605 # print "column_flags['unique']!=f.unique", column_flags['unique']!=f.unique 606 # print "column_flags['primary_key']!=f.primary_key", column_flags['primary_key']!=f.primary_key 607 col_type = data_types[data_type] 608 col_type_def = style.SQL_COLTYPE(col_type % f.__dict__) 609 # col_def = style.SQL_COLTYPE(col_type % f.__dict__) +' '+ style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or '')) 610 # if f.unique: 611 # col_def += ' '+ style.SQL_KEYWORD('UNIQUE') 612 # if f.primary_key: 613 # col_def += ' '+ style.SQL_KEYWORD('PRIMARY KEY') 614 output.append( backend.get_change_column_def_sql( db_table, cf, col_type_def, f.null, f.unique, f.primary_key ) ) 615 #print db_table, cf, f.maxlength, introspection.get_known_column_flags(cursor, db_table, cf) 616 return output 617 618 def get_sql_evolution_check_for_dead_fields(klass, new_table_name): 619 from django.db import backend, get_creation_module, models, get_introspection_module, connection 620 from django.db.models.fields import CharField, SlugField 621 from django.db.models.fields.related import RelatedField, ForeignKey 622 data_types = get_creation_module().DATA_TYPES 623 cursor = connection.cursor() 624 introspection = get_introspection_module() 625 opts = klass._meta 626 output = [] 627 db_table = klass._meta.db_table 628 if new_table_name: 629 db_table = new_table_name 630 suspect_fields = set(introspection.get_columns(cursor,db_table)) 631 for f in opts.fields: 632 suspect_fields.discard(f.column) 633 suspect_fields.discard(f.aka) 634 suspect_fields.difference_update(f.aka) 635 if len(suspect_fields)>0: 636 output.append( '-- warning: as the following may cause data loss, it/they must be run manually' ) 637 for suspect_field in suspect_fields: 638 output.append( backend.get_drop_column_sql( db_table, suspect_field ) ) 639 output.append( '-- end warning' ) 640 return output 416 641 417 642 def get_sql_all(app): … … 458 683 for model in model_list: 459 684 # Create the model's database table, if it doesn't already exist. 460 if model._meta.db_table in table_list :685 if model._meta.db_table in table_list or model._meta.aka in table_list or len(set(model._meta.aka) & set(table_list))>0: 461 686 continue 462 687 sql, references = _get_sql_model_create(model, seen_models) … … 482 707 cursor.execute(statement) 483 708 709 for sql in get_sql_evolution(app): 710 print sql 711 # cursor.execute(sql) 712 484 713 transaction.commit_unless_managed() 485 714 486 715 # Send the post_syncdb signal, so individual apps can do whatever they need 487 716 # to do at this point. … … 518 747 output.append('{%% if perms.%s %%}' % app_label) 519 748 output.append('<div class="module"><h2>%s</h2><table>' % app_label.title()) 520 for klassin app_models:521 if klass._meta.admin:749 for model in app_models: 750 if model._meta.admin: 522 751 output.append(MODULE_TEMPLATE % { 523 752 'app': app_label, 524 'mod': klass._meta.module_name,525 'name': capfirst( klass._meta.verbose_name_plural),526 'addperm': klass._meta.get_add_permission(),527 'changeperm': klass._meta.get_change_permission(),753 'mod': model._meta.module_name, 754 'name': capfirst(model._meta.verbose_name_plural), 755 'addperm': model._meta.get_add_permission(), 756 'changeperm': model._meta.get_change_permission(), 528 757 }) 529 758 output.append('</table></div>') … … 593 822 "Executes the equivalent of 'get_sql_reset' in the current database." 594 823 from django.db import connection, transaction 595 from cStringIO import StringIO596 824 app_name = app.__name__.split('.')[-2] 597 825 … … 693 921 "Generator that introspects the tables in the given database name and returns a Django model, one line at a time." 694 922 from django.db import connection, get_introspection_module 695 from django.conf import settings696 923 import keyword 697 924 698 925 introspection_module = get_introspection_module() 699 926 700 def table2model(table_name): 701 object_name = table_name.title().replace('_', '') 702 return object_name.endswith('s') and object_name[:-1] or object_name 927 table2model = lambda table_name: table_name.title().replace('_', '') 703 928 704 929 cursor = connection.cursor() … … 729 954 extra_params = {} # Holds Field parameters such as 'db_column'. 730 955 956 if ' ' in att_name: 957 extra_params['db_column'] = att_name 958 att_name = att_name.replace(' ', '') 959 comment_notes.append('Field renamed to remove spaces.') 731 960 if keyword.iskeyword(att_name): 732 961 extra_params['db_column'] = att_name … … 819 1048 820 1049 e = ModelErrorCollection(outfile) 821 1050 822 1051 for (app_name, error) in get_app_errors().items(): 823 1052 e.add(app_name, error) 824 1053 825 1054 for cls in models.get_models(app): 826 1055 opts = cls._meta … … 887 1116 e.add(opts, "Reverse query name for field '%s' clashes with related field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name)) 888 1117 889 1118 890 1119 for i, f in enumerate(opts.many_to_many): 891 1120 # Check to see if the related m2m field will clash with any … … 959 1188 except models.FieldDoesNotExist: 960 1189 e.add(opts, '"admin.list_filter" refers to %r, which isn\'t a field.' % fn) 1190 # date_hierarchy 1191 if opts.admin.date_hierarchy: 1192 try: 1193 f = opts.get_field(opts.admin.date_hierarchy) 1194 except models.FieldDoesNotExist: 1195 e.add(opts, '"admin.date_hierarchy" refers to %r, which isn\'t a field.' % opts.admin.date_hierarchy) 961 1196 962 1197 # Check ordering attribute. … … 1025 1260 sys.exit(1) 1026 1261 1027 def runserver(addr, port ):1262 def runserver(addr, port, use_reloader=True): 1028 1263 "Starts a lightweight Web server for development." 1029 1264 from django.core.servers.basehttp import run, AdminMediaHandler, WSGIServerException … … 1059 1294 except KeyboardInterrupt: 1060 1295 sys.exit(0) 1061 from django.utils import autoreload 1062 autoreload.main(inner_run) 1063 runserver.args = '[optional port number, or ipaddr:port]' 1296 if use_reloader: 1297 from django.utils import autoreload 1298 autoreload.main(inner_run) 1299 else: 1300 inner_run() 1301 runserver.args = '[--noreload] [optional port number, or ipaddr:port]' 1064 1302 1065 1303 def createcachetable(tablename): … … 1131 1369 1132 1370 def runfcgi(args): 1133 """Run this project as a FastCGI application. requires flup.""" 1371 "Runs this project as a FastCGI application. Requires flup." 1372 from django.conf import settings 1373 from django.utils import translation 1374 # Activate the current language, because it won't get activated later. 1375 try: 1376 translation.activate(settings.LANGUAGE_CODE) 1377 except AttributeError: 1378 pass 1134 1379 from django.core.servers.fastcgi import runfastcgi 1135 1380 runfastcgi(args) … … 1156 1401 'sqlreset': get_sql_reset, 1157 1402 'sqlsequencereset': get_sql_sequence_reset, 1403 'sqlevolve': get_sql_evolution, 1158 1404 'startapp': startapp, 1159 1405 'startproject': startproject, … … 1210 1456 parser.add_option('--plain', action='store_true', dest='plain', 1211 1457 help='Tells Django to use plain Python, not IPython, for "shell" command.') 1458 parser.add_option('--noreload', action='store_false', dest='use_reloader', default=True, 1459 help='Tells Django to NOT use the auto-reloader when running the development server.') 1212 1460 options, args = parser.parse_args(argv[1:]) 1213 1461 … … 1265 1513 except ValueError: 1266 1514 addr, port = '', args[1] 1267 action_mapping[action](addr, port )1515 action_mapping[action](addr, port, options.use_reloader) 1268 1516 elif action == 'runfcgi': 1269 1517 action_mapping[action](args[1:]) … … 1286 1534 print style.SQL_KEYWORD("COMMIT;") 1287 1535 1288 def execute_manager(settings_mod, argv=None): 1536 def setup_environ(settings_mod): 1537 """ 1538 Configure the runtime environment. This can also be used by external 1539 scripts wanting to set up a similar environment to manage.py. 1540 """ 1289 1541 # Add this project to sys.path so that it's importable in the conventional 1290 1542 # way. For example, if this file (manage.py) lives in a directory … … 1298 1550 # Set DJANGO_SETTINGS_MODULE appropriately. 1299 1551 os.environ['DJANGO_SETTINGS_MODULE'] = '%s.settings' % project_name 1300 1552 return project_directory 1553 1554 def execute_manager(settings_mod, argv=None): 1555 project_directory = setup_environ(settings_mod) 1301 1556 action_mapping = DEFAULT_ACTION_MAPPING.copy() 1302 1557 django/branches/schema-evolution/django/db/backends/mysql/base.py
r3115 r3646 41 41 try: 42 42 return self.cursor.executemany(sql, param_list) 43 except Database.Warning :43 except Database.Warning, w: 44 44 self.cursor.execute("SHOW WARNINGS") 45 45 raise Database.Warning, "%s: %s" % (w, self.cursor.fetchall()) … … 162 162 return "DEFAULT" 163 163 164 def get_change_column_name_sql( table_name, indexes, old_col_name, new_col_name, col_def ): 165 # mysql doesn't support column renames (AFAIK), so we fake it 166 # TODO: only supports a single primary key so far 167 pk_name = None 168 for key in indexes.keys(): 169 if indexes[key]['primary_key']: pk_name = key 170 output = [] 171 output.append( 'ALTER TABLE '+ quote_name(table_name) +' CHANGE COLUMN '+ quote_name(old_col_name) +' '+ quote_name(new_col_name) +' '+ col_def + ';' ) 172 return '\n'.join(output) 173 174 def get_change_column_def_sql( table_name, col_name, col_type, null, unique, primary_key ): 175 output = [] 176 col_def = col_type +' '+ ('%sNULL' % (not null and 'NOT ' or '')) 177 if unique: 178 col_def += ' '+ 'UNIQUE' 179 if primary_key: 180 col_def += ' '+ 'PRIMARY KEY' 181 output.append( 'ALTER TABLE '+ quote_name(table_name) +' MODIFY COLUMN '+ quote_name(col_name) +' '+ col_def + ';' ) 182 return '\n'.join(output) 183 184 def get_add_column_sql( table_name, col_name, col_type, null, unique, primary_key ): 185 output = [] 186 field_output = [] 187 field_output.append('ALTER TABLE') 188 field_output.append(quote_name(table_name)) 189 field_output.append('ADD COLUMN') 190 field_output.append(quote_name(col_name)) 191 field_output.append(col_type) 192 field_output.append(('%sNULL' % (not null and 'NOT ' or ''))) 193 if unique: 194 field_output.append(('UNIQUE')) 195 if primary_key: 196 field_output.append(('PRIMARY KEY')) 197 output.append(' '.join(field_output) + ';') 198 return '\n'.join(output) 199 200 def get_drop_column_sql( table_name, col_name ): 201 output = [] 202 output.append( '-- ALTER TABLE '+ quote_name(table_name) +' DROP COLUMN '+ quote_name(col_name) + ';' ) 203 return '\n'.join(output) 204 205 164 206 OPERATOR_MAPPING = { 165 207 'exact': '= %s', django/branches/schema-evolution/django/db/backends/mysql/introspection.py
r2922 r3646 1 from django.db import transaction2 1 from django.db.backends.mysql.base import quote_name 3 2 from MySQLdb import ProgrammingError, OperationalError … … 74 73 return indexes 75 74 75 def get_columns(cursor, table_name): 76 try: 77 cursor.execute("describe %s" % quote_name(table_name)) 78 return [row[0] for row in cursor.fetchall()] 79 except: 80 return [] 81 82 def get_known_column_flags( cursor, table_name, column_name ): 83 cursor.execute("describe %s" % quote_name(table_name)) 84 dict = {} 85 for row in cursor.fetchall(): 86 if row[0] == column_name: 87 88 # maxlength check goes here 89 if row[1][0:7]=='varchar': 90 dict['maxlength'] = row[1][8:len(row[1])-1] 91 92 # default flag check goes here 93 if row[2]=='YES': dict['allow_null'] = True 94 else: dict['allow_null'] = False 95 96 # primary/foreign/unique key flag check goes here 97 if row[3]=='PRI': dict['primary_key'] = True 98 else: dict['primary_key'] = False 99 if row[3]=='FOR': dict['foreign_key'] = True 100 else: dict['foreign_key'] = False 101 if row[3]=='UNI': dict['unique'] = True 102 else: dict['unique'] = False 103 104 # default value check goes here 105 # if row[4]=='NULL': dict['default'] = None 106 # else: dict['default'] = row[4] 107 dict['default'] = row[4] 108 109 # print table_name, column_name, dict 110 return dict 111 76 112 DATA_TYPES_REVERSE = { 77 113 FIELD_TYPE.BLOB: 'TextField', django/branches/schema-evolution/django/db/backends/postgresql/base.py
r3115 r3646 112 112 return "DEFAULT" 113 113 114 def get_change_column_name_sql( table_name, indexes, old_col_name, new_col_name, col_def ): 115 # TODO: only supports a single primary key so far 116 pk_name = None 117 for key in indexes.keys(): 118 if indexes[key]['primary_key']: pk_name = key 119 output = [] 120 output.append( 'ALTER TABLE '+ quote_name(table_name) +' RENAME COLUMN '+ quote_name(old_col_name) +' TO '+ quote_name(new_col_name) +';' ) 121 return '\n'.join(output) 122 123 def get_change_column_def_sql( table_name, col_name, col_type, null, unique, primary_key ): 124 output = [] 125 output.append( 'ALTER TABLE '+ quote_name(table_name) +' ADD COLUMN '+ quote_name(col_name+'_tmp') +' '+ col_type + ';' ) 126 output.append( 'UPDATE '+ quote_name(table_name) +' SET '+ quote_name(col_name+'_tmp') +' = '+ quote_name(col_name) + ';' ) 127 output.append( 'ALTER TABLE '+ quote_name(table_name) +' DROP COLUMN '+ quote_name(col_name) +';' ) 128 output.append( 'ALTER TABLE '+ quote_name(table_name) +' RENAME COLUMN '+ quote_name(col_name+'_tmp') +' TO '+ quote_name(col_name) + ';' ) 129 if not null: 130 output.append( 'ALTER TABLE '+ quote_name(table_name) +' ALTER COLUMN '+ quote_name(col_name) +' SET NOT NULL;' ) 131 if unique: 132 output.append( 'ALTER TABLE '+ quote_name(table_name) +' ADD CONSTRAINT '+ table_name +'_'+ col_name +'_unique_constraint UNIQUE('+ col_name +');' ) 133 134 return '\n'.join(output) 135 136 def get_add_column_sql( table_name, col_name, col_type, null, unique, primary_key ): 137 output = [] 138 output.append( 'ALTER TABLE '+ quote_name(table_name) +' ADD COLUMN '+ quote_name(col_name) +' '+ col_type + ';' ) 139 if not null: 140 output.append( 'ALTER TABLE '+ quote_name(table_name) +' ALTER COLUMN '+ quote_name(col_name) +' SET NOT NULL;' ) 141 if unique: 142 output.append( 'ALTER TABLE '+ quote_name(table_name) +' ADD CONSTRAINT '+ table_name +'_'+ col_name +'_unique_constraint UNIQUE('+ col_name +');' ) 143 return '\n'.join(output) 144 145 def get_drop_column_sql( table_name, col_name ): 146 output = [] 147 output.append( '-- ALTER TABLE '+ quote_name(table_name) +' DROP COLUMN '+ quote_name(col_name) + ';' ) 148 return '\n'.join(output) 149 114 150 # Register these custom typecasts, because Django expects dates/times to be 115 151 # in Python's native (standard-library) datetime/time format, whereas psycopg django/branches/schema-evolution/django/db/backends/postgresql/introspection.py
r3047 r3646 1 from django.db import transaction2 1 from django.db.backends.postgresql.base import quote_name 3 2 … … 68 67 return indexes 69 68 69 def get_columns(cursor, table_name): 70 try: 71 cursor.execute("SELECT a.attname, pg_catalog.format_type(a.atttypid, a.atttypmod), (SELECT substring(d.adsrc for 128) FROM pg_catalog.pg_attrdef d WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef), a.attnotnull, a.attnum, pg_catalog.col_description(a.attrelid, a.attnum) FROM pg_catalog.pg_attribute a WHERE a.attrelid = (SELECT c.oid from pg_catalog.pg_class c where c.relname ~ '^%s$') AND a.attnum > 0 AND NOT a.attisdropped ORDER BY a.attnum" % table_name) 72 return [row[0] for row in cursor.fetchall()] 73 except: 74 return [] 75 76 def get_known_column_flags( cursor, table_name, column_name ): 7
