def add_column(args, output): " <app> <model> <column> [<column2> ...]: Add one or more columns" if len(args) < 3: raise CommandError( './manage.py migration addcolumn <app> <model> <column> ' '[<column2> ...]' ) app_label, model, columns = args[0], args[1], args[2:] actual_model = models.get_model(app_label, model) style = no_style() sql, references = connection.creation.sql_create_model( actual_model, style, set() ) col_specs = [] for column in columns: is_foreign_key = isinstance( actual_model._meta.get_field_by_name(column)[0], models.ForeignKey ) col_specs.append(( column, extract_column_spec(sql[0], column, is_foreign_key), is_foreign_key )) migration_defs = [ add_column_mtemplate % (app_label, model, column, col_spec) for (column, col_spec, is_foreign_key) in col_specs if not is_foreign_key ] migration_fk_defs = [ add_column_foreignkey_mtemplate % ( app_label, model, column, col_spec, actual_model._meta.get_field_by_name(column)[0].rel.to._meta.db_table ) for (column, col_spec, is_foreign_key) in col_specs if is_foreign_key ] if migration_fk_defs: print >>sys.stderr, """Warning! You have added columns that are foreign keys (%s). These will be added as nullable. If you need them to be NOT NULL, then you have to write another migration to do that, after you've populated them with data.""" % ','.join([column for (column, x, fk) in col_specs if fk]) migration_defs += migration_fk_defs migration_output = migration_code(*migration_defs) if len(columns) == 1: migration_name = 'add_column_%s_to_%s_%s' % ( columns[0], app_label, model ) else: migration_name = 'add_columns_%s_to_%s_%s' % ( "_and_".join(columns), app_label, model ) save_migration(output, migration_output, migration_name)
def add_new(args, output): " <description>: Create empty migration (uses description in filename)" if not args: raise CommandError('./manage.py migration new <description>') save_migration( output, skeleton_template, '_'.join(args).lower() )
def add_new(args, output): " <description>: Create empty migration (uses description in filename)" if not args: raise CommandError('./manage.py migration new <description>') db_engine = getattr(settings, 'DMIGRATIONS_DATABASE_BACKEND', 'mysql') description = '_'.join(args).lower().replace('.', '_') save_migration( output, skeleton_template % db_engine, description )
def add_insert(args, output): " <app> <model>: Create insert migration for data in table" if len(args) != 2: raise CommandError('./manage.py migration insert <app> <model>') app_label, model = args table_name = '%s_%s' % (app_label, model) def get_columns(table_name): "Returns columns for table" cursor = connection.cursor() cursor.execute('describe %s' % table_name) rows = cursor.fetchall() cursor.close() # Sanity check that first column is called 'id' and is primary key first = rows[0] assert first[0] == u'id', 'First column must be id' assert first[3] == u'PRI', 'First column must be primary key' return [r[0] for r in rows] def get_dump(table_name): "Returns {'table_name':..., 'columns':..., 'rows':...}" columns = get_columns(table_name) # Escape column names with `backticks` - so columns with names that # match MySQL reserved words (e.g. "order") don't break things escaped_columns = ['`%s`' % column for column in columns] sql = 'SELECT %s FROM %s' % (', '.join(escaped_columns), table_name) cursor = connection.cursor() cursor.execute(sql) rows = cursor.fetchall() cursor.close() return { 'table_name': table_name, 'columns': columns, 'rows': rows, } dump = get_dump(table_name) migration_output = insert_mtemplate % { 'table_name': dump['table_name'], 'columns': repr(dump['columns']), 'insert_rows': pprint.pformat(dump['rows']), 'delete_ids': ', '.join(map(str, [r[0] for r in dump['rows']])), } migration_output = migration_code(migration_output) save_migration(output, migration_output, 'insert_into_%s_%s' % ( app_label, model ))
def rename_table(args, output): " <oldname> <newname>: Rename table" if len(args) != 2: raise CommandError('./manage.py dmigration renametable <oldname> <newname>') oldname, newname = args migration_output = 'm.RenameTable(%r, %r)' % (oldname, newname) migration_output = migration_code(migration_output) save_migration(output, migration_output, 'rename_table_%s_%s' % ( oldname, newname ))
def add_index(args, output): " <app> <model> <column>: Add an index" if len(args) != 3: raise CommandError( './manage.py migration addindex <app> <model> <column>' ) app_label, model, column = args migration_output = add_index_mtemplate % (app_label, model, column) migration_output = migration_code(migration_output) save_migration(output, migration_output, 'add_index_%s_%s_%s' % ( app_label, model, column ))
def add_app(args, output): " <app>: Add tables for a new application" if len(args) != 1: raise CommandError('./manage.py migration app <name-of-app>') app_label = args[0] app = models.get_app(app_label) from django.core.management.sql import sql_create up_sql = sql_create(app, no_style(), connections[DEFAULT_DB_ALIAS]) down_sql = sql_delete(app, no_style()) app_name = app.__name__.replace('.', '_') migration_output = app_mtemplate % ( clean_up_create_sql(up_sql), clean_up_create_sql(down_sql) ) migration_output = migration_code(migration_output) save_migration(output, migration_output, app_name)
def add_table(args, output): " <app> <model>: Add tables for a new model" if len(args) != 2: raise CommandError('./manage.py migration app <name-of-app>') app_label, model = args app = models.get_app(app_label) app_name = app.__name__.replace('.', '_') model_to_add = models.get_model(app_label, model) if not model_to_add: raise Exception("Model %s in app %s not found" % (model, app_label)) # The following code is a bit of a mess. I copied it from # django.core.management.sql.sql_create without a full understanding of # how it all works. Ideally this needs to refactored in Django itself to # make it easier for libraries such as this one to reuse the table # creation logic. style = no_style() app_models = models.get_models(app) up_sql = [] tables = connection.introspection.table_names() known_models = set([ model for model in connection.introspection.installed_models(tables) ]) pending_references = {} sql_output, references = connection.creation.sql_create_model( model_to_add, style, known_models ) up_sql.extend(sql_output) for refto, refs in references.items(): pending_references.setdefault(refto, []).extend(refs) if refto not in known_models: logging.warning('FK to %s, a model not backed by a db table.' % refto) up_sql.extend( connection.creation.sql_for_pending_references( refto, style, pending_references ) ) up_sql.extend( connection.creation.sql_for_pending_references( model, style, pending_references ) ) # Keep track of the fact that we've created the table for this model. known_models.add(model_to_add) # Create the many-to-many join tables. for field in model_to_add._meta.fields: try: table, deferred = connection.creation.sql_for_inline_many_to_many_references(model_to_add, field, style) assert False, "It's not clear that this is working" up_sql.extend(table) except AttributeError: pass if not up_sql: raise Exception("Model %s in app %s not found" % (model, app_label)) # Down sql just drops any tables we have created down_sql = [] for sql in up_sql: if sql.startswith('CREATE TABLE'): down_sql.append('DROP TABLE %s;' % sql.split()[2]) # Reverse the order of down_sql down_sql = down_sql[::-1] migration_output = app_mtemplate % ( clean_up_create_sql(up_sql), clean_up_create_sql(down_sql) ) migration_output = migration_code(migration_output) save_migration(output, migration_output, app_name)
def add_column(args, output): " <app> <model> <column> [<column2> ...]: Add one or more columns" if len(args) < 3: raise CommandError( './manage.py migration addcolumn <app> <model> <column> ' '[<column2> ...]' ) app_label, model, columns = args[0], args[1], args[2:] actual_model = models.get_model(app_label, model) table_name = actual_model._meta.db_table if actual_model is None: raise CommandError( 'No such model %r, or it is not a concrete model.' % model ) if table_name.startswith(app_label+'_'): table_name = table_name[len(app_label)+1:] style = no_style() sql, references = connection.creation.sql_create_model( actual_model, style, set() ) col_specs = [] for column in columns: is_foreign_key = isinstance( actual_model._meta.get_field_by_name(column)[0], models.ForeignKey ) col_specs.append(( column, extract_column_spec(sql[0], column, is_foreign_key), is_foreign_key )) migration_defs = [ add_column_mtemplate % (app_label, table_name, column, col_spec) for (column, col_spec, is_foreign_key) in col_specs if not is_foreign_key ] migration_fk_defs = [ add_column_foreignkey_mtemplate % ( app_label, model, column, col_spec, actual_model._meta.get_field_by_name(column)[0].rel.to._meta.db_table ) for (column, col_spec, is_foreign_key) in col_specs if is_foreign_key ] if migration_fk_defs: print >>sys.stderr, """Warning! You have added columns that are foreign keys (%s). These will be added as nullable. If you need them to be NOT NULL, then you have to write another migration to do that, after you've populated them with data.""" % ','.join([column for (column, x, fk) in col_specs if fk]) migration_defs += migration_fk_defs migration_output = migration_code(*migration_defs) if len(columns) == 1: migration_name = 'add_column_%s_to_%s_%s' % ( columns[0], app_label, model ) else: migration_name = 'add_columns_%s_to_%s_%s' % ( "_and_".join(columns), app_label, model ) save_migration(output, migration_output, migration_name)