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)
Example #2
0
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()
    )
Example #3
0
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
    )
Example #4
0
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
    ))
Example #5
0
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
    ))
Example #6
0
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
    ))
Example #7
0
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)
Example #8
0
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)
Example #9
0
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)