def get_sql_evolution_check_for_changed_field_name(klass, old_table_name, style):
    from django.db import get_introspection_module, connection
    
    ops, introspection = get_operations_and_introspection_classes(style)

    cursor = connection.cursor()
    opts = klass._meta
    output = []
    db_table = klass._meta.db_table
    if old_table_name: 
        db_table = old_table_name
    for f in opts.fields:
        existing_fields = introspection.get_columns(cursor,db_table)
        if f.column in existing_fields:
            old_col = f.column
        elif f.aka and len(set(f.aka).intersection(set(existing_fields)))==1:
            old_col = set(f.aka).intersection(set(existing_fields)).pop()
        elif f.aka and len(set(f.aka).intersection(set(existing_fields)))>1:
            details = 'column "%s" of table "%s"' % (f.column, klass._meta.db_table)
            raise MultipleRenamesPossibleException("when renamed " + details)
        else:
            continue
        if old_col != f.column:
            col_type = f.db_type()
            col_type_def = style.SQL_COLTYPE(col_type)
            if col_type is not None:
                col_def = style.SQL_COLTYPE(col_type) +' '+ style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or ''))
                if f.unique and not f.primary_key:
                    col_def += style.SQL_KEYWORD(' UNIQUE')
                if f.primary_key:
                    col_def += style.SQL_KEYWORD(' PRIMARY KEY')
                output.extend( ops.get_change_column_name_sql( klass._meta.db_table, get_introspection_module().get_indexes(cursor,db_table), old_col, f.column, col_type_def, f ) )
    return output
def get_sql_evolution_rebuild_table(klass, old_table_name, style):
    from django.db import get_introspection_module, connection
    
    ops, introspection = get_operations_and_introspection_classes(style)

    cursor = connection.cursor()
    opts = klass._meta
    output = []
    db_table = klass._meta.db_table
    if old_table_name: 
        db_table = old_table_name

    existing_fields = introspection.get_columns(cursor,db_table)
    renamed_fields = {}
    
    for f in opts.fields:
        if f.column in existing_fields:
            old_col = f.column
        elif f.aka and len(set(f.aka).intersection(set(existing_fields)))==1:
            old_col = set(f.aka).intersection(set(existing_fields)).pop()
        elif f.aka and len(set(f.aka).intersection(set(existing_fields)))>1:
            details = 'column "%s" of table "%s"' % (f.column, klass._meta.db_table)
            raise MultipleRenamesPossibleException("when renamed " + details)
        else:
            continue
        if old_col != f.column:
            renamed_fields[f.column] = old_col
    
    output.extend( ops.get_rebuild_table_sql( klass._meta.db_table, get_introspection_module().get_indexes(cursor,db_table), existing_fields, renamed_fields) )
    return output
Example #3
0
    def __init__(self, app_models, options):
        self.app_models = app_models
        self.options = options
        self.dense = options.get('dense_output', False)

        try:
            self.introspection = connection.introspection
        except AttributeError:
            from django.db import get_introspection_module
            self.introspection = get_introspection_module()

        self.cursor = connection.cursor()
        self.django_tables = self.get_django_tables(
            options.get('only_existing', True))
        self.db_tables = self.introspection.get_table_list(self.cursor)
        self.differences = []
        self.unknown_db_fields = {}

        self.DIFF_SQL = {
            'error': self.SQL_ERROR,
            'comment': self.SQL_COMMENT,
            'table-missing-in-db': self.SQL_TABLE_MISSING_IN_DB,
            'field-missing-in-db': self.SQL_FIELD_MISSING_IN_DB,
            'field-missing-in-model': self.SQL_FIELD_MISSING_IN_MODEL,
            'index-missing-in-db': self.SQL_INDEX_MISSING_IN_DB,
            'index-missing-in-model': self.SQL_INDEX_MISSING_IN_MODEL,
            'unique-missing-in-db': self.SQL_UNIQUE_MISSING_IN_DB,
            'unique-missing-in-model': self.SQL_UNIQUE_MISSING_IN_MODEL,
            'field-type-differ': self.SQL_FIELD_TYPE_DIFFER,
            'field-parameter-differ': self.SQL_FIELD_PARAMETER_DIFFER,
            'notnull-differ': self.SQL_NOTNULL_DIFFER,
        }
    def __init__(self, app_models, options):
        self.app_models = app_models
        self.options = options
        self.dense = options.get('dense_output', False)

        try:
            self.introspection = connection.introspection
        except AttributeError:
            from django.db import get_introspection_module
            self.introspection = get_introspection_module()

        self.cursor = connection.cursor()
        self.django_tables = self.get_django_tables(options.get('only_existing', True))
        self.db_tables = self.introspection.get_table_list(self.cursor)
        self.differences = []
        self.unknown_db_fields = {}

        self.DIFF_SQL = {
            'comment': self.SQL_COMMENT,
            'table-missing-in-db': self.SQL_TABLE_MISSING_IN_DB,
            'field-missing-in-db': self.SQL_FIELD_MISSING_IN_DB,
            'field-missing-in-model': self.SQL_FIELD_MISSING_IN_MODEL,
            'index-missing-in-db': self.SQL_INDEX_MISSING_IN_DB,
            'index-missing-in-model': self.SQL_INDEX_MISSING_IN_MODEL,
            'unique-missing-in-db': self.SQL_UNIQUE_MISSING_IN_DB,
            'unique-missing-in-model': self.SQL_UNIQUE_MISSING_IN_MODEL,
            'field-type-differ': self.SQL_FIELD_TYPE_DIFFER,
            'field-parameter-differ': self.SQL_FIELD_PARAMETER_DIFFER,
        }
Example #5
0
    def __init__(self, app_models, options):
        self.has_differences = None
        self.app_models = app_models
        self.options = options
        self.dense = options.get('dense_output', False)

        try:
            self.introspection = connection.introspection
        except AttributeError:
            from django.db import get_introspection_module
            self.introspection = get_introspection_module()

        self.cursor = connection.cursor()
        self.django_tables = self.get_django_tables(options.get('only_existing', True))
        self.db_tables = self.introspection.get_table_list(self.cursor)
        if django.VERSION[:2] >= (1, 8):
            # TODO: We are losing information about tables which are views here
            self.db_tables = [table_info.name for table_info in self.db_tables]
        self.differences = []
        self.unknown_db_fields = {}
        self.new_db_fields = set()
        self.null = {}
        self.unsigned = set()

        self.DIFF_SQL = {
            'error': self.SQL_ERROR,
            'comment': self.SQL_COMMENT,
            'table-missing-in-db': self.SQL_TABLE_MISSING_IN_DB,
            'table-missing-in-model': self.SQL_TABLE_MISSING_IN_MODEL,
            'field-missing-in-db': self.SQL_FIELD_MISSING_IN_DB,
            'field-missing-in-model': self.SQL_FIELD_MISSING_IN_MODEL,
            'fkey-missing-in-db': self.SQL_FKEY_MISSING_IN_DB,
            'fkey-missing-in-model': self.SQL_FIELD_MISSING_IN_MODEL,
            'index-missing-in-db': self.SQL_INDEX_MISSING_IN_DB,
            'index-missing-in-model': self.SQL_INDEX_MISSING_IN_MODEL,
            'unique-missing-in-db': self.SQL_UNIQUE_MISSING_IN_DB,
            'unique-missing-in-model': self.SQL_UNIQUE_MISSING_IN_MODEL,
            'field-type-differ': self.SQL_FIELD_TYPE_DIFFER,
            'field-parameter-differ': self.SQL_FIELD_PARAMETER_DIFFER,
            'notnull-differ': self.SQL_NOTNULL_DIFFER,
        }

        if self.can_detect_notnull_differ:
            self.load_null()

        if self.can_detect_unsigned_differ:
            self.load_unsigned()
Example #6
0
    def __init__(self, app_models, options):
        self.has_differences = None
        self.app_models = app_models
        self.options = options
        self.dense = options.get('dense_output', False)

        try:
            self.introspection = connection.introspection
        except AttributeError:
            from django.db import get_introspection_module
            self.introspection = get_introspection_module()

        self.cursor = connection.cursor()
        self.django_tables = self.get_django_tables(options.get('only_existing', True))
        # TODO: We are losing information about tables which are views here
        self.db_tables = [table_info.name for table_info in self.introspection.get_table_list(self.cursor)]
        self.differences = []
        self.unknown_db_fields = {}
        self.new_db_fields = set()
        self.null = {}
        self.unsigned = set()

        self.DIFF_SQL = {
            'error': self.SQL_ERROR,
            'comment': self.SQL_COMMENT,
            'table-missing-in-db': self.SQL_TABLE_MISSING_IN_DB,
            'table-missing-in-model': self.SQL_TABLE_MISSING_IN_MODEL,
            'field-missing-in-db': self.SQL_FIELD_MISSING_IN_DB,
            'field-missing-in-model': self.SQL_FIELD_MISSING_IN_MODEL,
            'fkey-missing-in-db': self.SQL_FKEY_MISSING_IN_DB,
            'fkey-missing-in-model': self.SQL_FIELD_MISSING_IN_MODEL,
            'index-missing-in-db': self.SQL_INDEX_MISSING_IN_DB,
            'index-missing-in-model': self.SQL_INDEX_MISSING_IN_MODEL,
            'unique-missing-in-db': self.SQL_UNIQUE_MISSING_IN_DB,
            'unique-missing-in-model': self.SQL_UNIQUE_MISSING_IN_MODEL,
            'field-type-differ': self.SQL_FIELD_TYPE_DIFFER,
            'field-parameter-differ': self.SQL_FIELD_PARAMETER_DIFFER,
            'notnull-differ': self.SQL_NOTNULL_DIFFER,
        }

        if self.can_detect_notnull_differ:
            self.load_null()

        if self.can_detect_unsigned_differ:
            self.load_unsigned()
Example #7
0
    def __init__(self, app_models, options):
        self.has_differences = None
        self.app_models = app_models
        self.options = options
        self.dense = options.get("dense_output", False)

        try:
            self.introspection = connection.introspection
        except AttributeError:
            from django.db import get_introspection_module

            self.introspection = get_introspection_module()

        self.cursor = connection.cursor()
        self.django_tables = self.get_django_tables(options.get("only_existing", True))
        self.db_tables = self.introspection.get_table_list(self.cursor)
        self.differences = []
        self.unknown_db_fields = {}
        self.new_db_fields = set()
        self.null = {}
        self.unsigned = set()

        self.DIFF_SQL = {
            "error": self.SQL_ERROR,
            "comment": self.SQL_COMMENT,
            "table-missing-in-db": self.SQL_TABLE_MISSING_IN_DB,
            "table-missing-in-model": self.SQL_TABLE_MISSING_IN_MODEL,
            "field-missing-in-db": self.SQL_FIELD_MISSING_IN_DB,
            "field-missing-in-model": self.SQL_FIELD_MISSING_IN_MODEL,
            "fkey-missing-in-db": self.SQL_FKEY_MISSING_IN_DB,
            "fkey-missing-in-model": self.SQL_FIELD_MISSING_IN_MODEL,
            "index-missing-in-db": self.SQL_INDEX_MISSING_IN_DB,
            "index-missing-in-model": self.SQL_INDEX_MISSING_IN_MODEL,
            "unique-missing-in-db": self.SQL_UNIQUE_MISSING_IN_DB,
            "unique-missing-in-model": self.SQL_UNIQUE_MISSING_IN_MODEL,
            "field-type-differ": self.SQL_FIELD_TYPE_DIFFER,
            "field-parameter-differ": self.SQL_FIELD_PARAMETER_DIFFER,
            "notnull-differ": self.SQL_NOTNULL_DIFFER,
        }

        if self.can_detect_notnull_differ:
            self.load_null()

        if self.can_detect_unsigned_differ:
            self.load_unsigned()
def get_sql_evolution_check_for_changed_model_name(klass, style):
    from django.db import get_introspection_module, connection

    ops, introspection = get_operations_and_introspection_classes(style)

    cursor = connection.cursor()
    introspection = get_introspection_module()
#    ops = get_ops_class(connection)
    table_list = introspection.get_table_list(cursor)
    if klass._meta.db_table in table_list:
        return [], None
    aka_db_tables = set()
    if klass._meta.aka:
        for x in klass._meta.aka:
            aka_db_tables.add( "%s_%s" % (klass._meta.app_label, x.lower()) )
    matches = list(aka_db_tables & set(table_list))
    if len(matches)==1:
        return ops.get_change_table_name_sql( klass._meta.db_table, matches[0]), matches[0]
    else:
        return [], None
Example #9
0
    def handle_inspection(self):
        from django.db import connection, get_introspection_module
        import keyword

        introspection_module = get_introspection_module()

        table2model = lambda table_name: table_name.title().replace('_', '')

        cursor = connection.cursor()
        yield "# This is an auto-generated Django model module."
        yield "# You'll have to do the following manually to clean this up:"
        yield "#     * Rearrange models' order"
        yield "#     * Make sure each model has one field with primary_key=True"
        yield "# Feel free to rename the models, but don't rename db_table values or field names."
        yield "#"
        yield "# Also note: You'll have to insert the output of 'django-admin.py sqlcustom [appname]'"
        yield "# into your database."
        yield ''
        yield 'from django.db import models'
        yield ''
        for table_name in introspection_module.get_table_list(cursor):
            yield 'class %s(models.Model):' % table2model(table_name)
            try:
                relations = introspection_module.get_relations(cursor, table_name)
            except NotImplementedError:
                relations = {}
            try:
                indexes = introspection_module.get_indexes(cursor, table_name)
            except NotImplementedError:
                indexes = {}
            for i, row in enumerate(introspection_module.get_table_description(cursor, table_name)):
                att_name = row[0].lower()
                comment_notes = [] # Holds Field notes, to be displayed in a Python comment.
                extra_params = {}  # Holds Field parameters such as 'db_column'.

                if ' ' in att_name:
                    extra_params['db_column'] = att_name
                    att_name = att_name.replace(' ', '')
                    comment_notes.append('Field renamed to remove spaces.')
                if keyword.iskeyword(att_name):
                    extra_params['db_column'] = att_name
                    att_name += '_field'
                    comment_notes.append('Field renamed because it was a Python reserved word.')

                if i in relations:
                    rel_to = relations[i][1] == table_name and "'self'" or table2model(relations[i][1])
                    field_type = 'ForeignKey(%s' % rel_to
                    if att_name.endswith('_id'):
                        att_name = att_name[:-3]
                    else:
                        extra_params['db_column'] = att_name
                else:
                    try:
                        field_type = introspection_module.DATA_TYPES_REVERSE[row[1]]
                    except KeyError:
                        field_type = 'TextField'
                        comment_notes.append('This field type is a guess.')

                    # This is a hook for DATA_TYPES_REVERSE to return a tuple of
                    # (field_type, extra_params_dict).
                    if type(field_type) is tuple:
                        field_type, new_params = field_type
                        extra_params.update(new_params)

                    # Add max_length for all CharFields.
                    if field_type == 'CharField' and row[3]:
                        extra_params['max_length'] = row[3]

                    if field_type == 'DecimalField':
                        extra_params['max_digits'] = row[4]
                        extra_params['decimal_places'] = row[5]

                    # Add primary_key and unique, if necessary.
                    column_name = extra_params.get('db_column', att_name)
                    if column_name in indexes:
                        if indexes[column_name]['primary_key']:
                            extra_params['primary_key'] = True
                        elif indexes[column_name]['unique']:
                            extra_params['unique'] = True

                    field_type += '('

                # Don't output 'id = meta.AutoField(primary_key=True)', because
                # that's assumed if it doesn't exist.
                if att_name == 'id' and field_type == 'AutoField(' and extra_params == {'primary_key': True}:
                    continue

                # Add 'null' and 'blank', if the 'null_ok' flag was present in the
                # table description.
                if row[6]: # If it's NULL...
                    extra_params['blank'] = True
                    if not field_type in ('TextField(', 'CharField('):
                        extra_params['null'] = True

                field_desc = '%s = models.%s' % (att_name, field_type)
                if extra_params:
                    if not field_desc.endswith('('):
                        field_desc += ', '
                    field_desc += ', '.join(['%s=%r' % (k, v) for k, v in list(extra_params.items())])
                field_desc += ')'
                if comment_notes:
                    field_desc += ' # ' + ' '.join(comment_notes)
                yield '    %s' % field_desc
            yield '    class Meta:'
            yield '        db_table = %r' % table_name
            yield ''
Example #10
0
def sql_delete(app, style):
    "Returns a list of the DROP TABLE SQL statements for the given app."
    from django.db import connection, models, get_introspection_module
    from django.db.backends.util import truncate_name
    from django.contrib.contenttypes import generic
    introspection = get_introspection_module()

    # This should work even if a connection isn't available
    try:
        cursor = connection.cursor()
    except:
        cursor = None

    # Figure out which tables already exist
    if cursor:
        table_names = introspection.get_table_list(cursor)
    else:
        table_names = []
    if connection.features.uses_case_insensitive_names:
        table_name_converter = lambda x: x.upper()
    else:
        table_name_converter = lambda x: x

    output = []
    qn = connection.ops.quote_name

    # Output DROP TABLE statements for standard application tables.
    to_delete = set()

    references_to_delete = {}
    app_models = models.get_models(app)
    for model in app_models:
        if cursor and table_name_converter(model._meta.db_table) in table_names:
            # The table exists, so it needs to be dropped
            opts = model._meta
            for f in opts.fields:
                if f.rel and f.rel.to not in to_delete:
                    references_to_delete.setdefault(f.rel.to, []).append( (model, f) )

            to_delete.add(model)

    for model in app_models:
        if cursor and table_name_converter(model._meta.db_table) in table_names:
            # Drop the table now
            output.append('%s %s;' % (style.SQL_KEYWORD('DROP TABLE'),
                style.SQL_TABLE(qn(model._meta.db_table))))
            if connection.features.supports_constraints and model in references_to_delete:
                for rel_class, f in references_to_delete[model]:
                    table = rel_class._meta.db_table
                    col = f.column
                    r_table = model._meta.db_table
                    r_col = model._meta.get_field(f.rel.field_name).column
                    r_name = '%s_refs_%s_%x' % (col, r_col, abs(hash((table, r_table))))
                    output.append('%s %s %s %s;' % \
                        (style.SQL_KEYWORD('ALTER TABLE'),
                        style.SQL_TABLE(qn(table)),
                        style.SQL_KEYWORD(connection.ops.drop_foreignkey_sql()),
                        style.SQL_FIELD(truncate_name(r_name, connection.ops.max_name_length()))))
                del references_to_delete[model]
            if model._meta.has_auto_field:
                ds = connection.ops.drop_sequence_sql(model._meta.db_table)
                if ds:
                    output.append(ds)

    # Output DROP TABLE statements for many-to-many tables.
    for model in app_models:
        opts = model._meta
        for f in opts.many_to_many:
            if isinstance(f.rel, generic.GenericRel):
                continue
            if cursor and table_name_converter(f.m2m_db_table()) in table_names:
                output.append("%s %s;" % (style.SQL_KEYWORD('DROP TABLE'),
                    style.SQL_TABLE(qn(f.m2m_db_table()))))
                ds = connection.ops.drop_sequence_sql("%s_%s" % (model._meta.db_table, f.column))
                if ds:
                    output.append(ds)

    app_label = app_models[0]._meta.app_label

    # Close database connection explicitly, in case this output is being piped
    # directly into a database client, to avoid locking issues.
    if cursor:
        cursor.close()
        connection.close()

    return output[::-1] # Reverse it, to deal with table dependencies.
Example #11
0
    def handle_inspection(self):
        from django.db import connection, get_introspection_module
        import keyword

        introspection_module = get_introspection_module()

        table2model = lambda table_name: table_name.title().replace('_', '')

        cursor = connection.cursor()
        yield "# This is an auto-generated Django model module."
        yield "# You'll have to do the following manually to clean this up:"
        yield "#     * Rearrange models' order"
        yield "#     * Make sure each model has one field with primary_key=True"
        yield "# Feel free to rename the models, but don't rename db_table values or field names."
        yield "#"
        yield "# Also note: You'll have to insert the output of 'django-admin.py sqlcustom [appname]'"
        yield "# into your database."
        yield ''
        yield 'from django.db import models'
        yield ''
        for table_name in introspection_module.get_table_list(cursor):
            yield 'class %s(models.Model):' % table2model(table_name)
            try:
                relations = introspection_module.get_relations(
                    cursor, table_name)
            except NotImplementedError:
                relations = {}
            try:
                indexes = introspection_module.get_indexes(cursor, table_name)
            except NotImplementedError:
                indexes = {}
            for i, row in enumerate(
                    introspection_module.get_table_description(
                        cursor, table_name)):
                att_name = row[0].lower()
                comment_notes = [
                ]  # Holds Field notes, to be displayed in a Python comment.
                extra_params = {
                }  # Holds Field parameters such as 'db_column'.

                if ' ' in att_name:
                    extra_params['db_column'] = att_name
                    att_name = att_name.replace(' ', '')
                    comment_notes.append('Field renamed to remove spaces.')
                if keyword.iskeyword(att_name):
                    extra_params['db_column'] = att_name
                    att_name += '_field'
                    comment_notes.append(
                        'Field renamed because it was a Python reserved word.')

                if i in relations:
                    rel_to = relations[i][
                        1] == table_name and "'self'" or table2model(
                            relations[i][1])
                    field_type = 'ForeignKey(%s' % rel_to
                    if att_name.endswith('_id'):
                        att_name = att_name[:-3]
                    else:
                        extra_params['db_column'] = att_name
                else:
                    try:
                        field_type = introspection_module.DATA_TYPES_REVERSE[
                            row[1]]
                    except KeyError:
                        field_type = 'TextField'
                        comment_notes.append('This field type is a guess.')

                    # This is a hook for DATA_TYPES_REVERSE to return a tuple of
                    # (field_type, extra_params_dict).
                    if type(field_type) is tuple:
                        field_type, new_params = field_type
                        extra_params.update(new_params)

                    # Add max_length for all CharFields.
                    if field_type == 'CharField' and row[3]:
                        extra_params['max_length'] = row[3]

                    if field_type == 'DecimalField':
                        extra_params['max_digits'] = row[4]
                        extra_params['decimal_places'] = row[5]

                    # Add primary_key and unique, if necessary.
                    column_name = extra_params.get('db_column', att_name)
                    if column_name in indexes:
                        if indexes[column_name]['primary_key']:
                            extra_params['primary_key'] = True
                        elif indexes[column_name]['unique']:
                            extra_params['unique'] = True

                    field_type += '('

                # Don't output 'id = meta.AutoField(primary_key=True)', because
                # that's assumed if it doesn't exist.
                if att_name == 'id' and field_type == 'AutoField(' and extra_params == {
                        'primary_key': True
                }:
                    continue

                # Add 'null' and 'blank', if the 'null_ok' flag was present in the
                # table description.
                if row[6]:  # If it's NULL...
                    extra_params['blank'] = True
                    if not field_type in ('TextField(', 'CharField('):
                        extra_params['null'] = True

                field_desc = '%s = models.%s' % (att_name, field_type)
                if extra_params:
                    if not field_desc.endswith('('):
                        field_desc += ', '
                    field_desc += ', '.join(
                        ['%s=%r' % (k, v) for k, v in extra_params.items()])
                field_desc += ')'
                if comment_notes:
                    field_desc += ' # ' + ' '.join(comment_notes)
                yield '    %s' % field_desc
            yield '    class Meta:'
            yield '        db_table = %r' % table_name
            yield ''
Example #12
0
def table_list():
    "Returns a list of all table names that exist in the database."
    from django.db import connection, get_introspection_module
    cursor = connection.cursor()
    return get_introspection_module().get_table_list(cursor)
def get_introspected_evolution_options(app, style):
    ops, introspection = get_operations_and_introspection_classes(style)
    from django.db import models, get_introspection_module, connection
    cursor = connection.cursor()
    app_name = app.__name__.split('.')[-2]

    final_output = []

    table_list = get_introspection_module().get_table_list(cursor)
    seen_models = management.installed_models(table_list)
    created_models = set()
    final_output = []
    
    seen_tables = set()

    model_list = models.get_models(app)
    for model in model_list:
        # Create the model's database table, if it doesn't already exist.
        aka_db_tables = set()
        if model._meta.aka:
            for x in model._meta.aka:
                aka_db_tables.add( "%s_%s" % (model._meta.app_label, x.lower()) )
        if model._meta.db_table in table_list or len(aka_db_tables & set(table_list))>0:
            continue #renamed
        sql, references = fixed_sql_model_create(model, seen_models, style)
        final_output.extend(sql)
        seen_models.add(model)
        created_models.add(model)
        table_list.append(model._meta.db_table)
        seen_tables.add(model._meta.db_table)

    # get the existing models, minus the models we've just created
    app_models = models.get_models(app)
    for model in created_models:
        if model in app_models:
            app_models.remove(model)

    for model in app_models:
        if model._meta.db_table:
            seen_tables.add(model._meta.db_table)
        output, old_table_name = get_sql_evolution_check_for_changed_model_name(model, style)
        if old_table_name: seen_tables.add(old_table_name)
        final_output.extend(output)
        
        rebuild = False
        output = get_sql_evolution_check_for_changed_field_flags(model, old_table_name, style)
        if output and settings.DATABASE_ENGINE == 'sqlite3': rebuild = True
        final_output.extend(output)
    
        output = get_sql_evolution_check_for_changed_field_name(model, old_table_name, style)
        final_output.extend(output)
        if output and settings.DATABASE_ENGINE == 'sqlite3': rebuild = True
        
        output = get_sql_evolution_check_for_new_fields(model, old_table_name, style)
        if output and settings.DATABASE_ENGINE == 'sqlite3': rebuild = True
        final_output.extend(output)
        
        output = get_sql_evolution_check_for_dead_fields(model, old_table_name, style)
        if output and settings.DATABASE_ENGINE == 'sqlite3': rebuild = True
        final_output.extend(output)
        
        if rebuild:
            output = get_sql_evolution_rebuild_table(model, old_table_name, style)
            final_output.extend(output)

    for model in app_models + list(created_models):
        for f in model._meta.many_to_many:
            #creating many_to_many table
            if not f.m2m_db_table() in get_introspection_module().get_table_list(cursor):
                final_output.extend( _get_many_to_many_sql_for_field(model, f, style) )
                seen_tables.add(f.m2m_db_table())
    output = get_sql_evolution_check_for_dead_models(table_list, seen_tables, app_name, app_models, style)
    final_output.extend(output)

    return final_output
Example #14
0
    def handle_diff(self, app, **options):
        from django.db import models, connection
        from django.core.management import sql as _sql
        
        app_name = app.__name__.split('.')[-2]
        
	try:
	    django_tables = connection.introspection.django_table_names(only_existing=options.get('only_existing', True))
	except AttributeError:
	    # backwards compatibility for before introspection refactoring (r8296)
    	    try:
        	django_tables = _sql.django_table_names(only_existing=options.get('only_existing', True))
    	    except AttributeError:
        	# backwards compatibility for before svn r7568 
    	        django_tables = _sql.django_table_list(only_existing=options.get('only_existing', True))
        django_tables = [django_table for django_table in django_tables if django_table.startswith(app_name)]
        
        app_models = models.get_models(app)
        if not app_models:
            return
        
	try:
	    from django.db import get_introspection_module
            introspection_module = get_introspection_module()
	except ImportError:
	    introspection_module = connection.introspection
	
        cursor = connection.cursor()
        model_diffs = []
        for app_model in app_models:
            _constraints = None
            _meta = app_model._meta
            table_name = _meta.db_table
            table_indexes = introspection_module.get_indexes(cursor, table_name)
	    
            fieldmap = dict([(field.get_attname(), field) for field in _meta.fields])

            if _meta.order_with_respect_to:
                fieldmap['_order'] = ORDERING_FIELD

            try:
                table_description = introspection_module.get_table_description(cursor, table_name)
            except Exception, e:
                model_diffs.append((app_model.__name__, [str(e).strip()]))
                transaction.rollback() # reset transaction
                continue
            diffs = []
            for i, row in enumerate(table_description):
                att_name = row[0].lower()
		try:
        	    db_field_reverse_type = introspection_module.data_types_reverse[row[1]]
		except AttributeError:
		    # backwards compatibility for before introspection refactoring (r8296)
		    db_field_reverse_type = introspection_module.DATA_TYPES_REVERSE.get(row[1])
                kwargs = {}
		if isinstance(db_field_reverse_type, tuple):
		    kwargs.update(db_field_reverse_type[1])
		    db_field_reverse_type = db_field_reverse_type[0]
		
                if db_field_reverse_type == "CharField" and row[3]:
                    kwargs['max_length'] = row[3]
		
                if db_field_reverse_type == "DecimalField":
                    kwargs['max_digits'] = row[4]
                    kwargs['decimal_places'] = row[5]
		
                if row[6]:
                    kwargs['blank'] = True
                    if not db_field_reverse_type in ('TextField', 'CharField'):
                        kwargs['null'] = True

                if fieldmap.has_key(att_name):
                    field = fieldmap.pop(att_name)
                    # check type
                    def clean(s):
                        s = s.split(" ")[0]
                        s = s.split("(")[0]
                        return s
                    def cmp_or_serialcmp(x, y):
                        result = x==y
                        if result:
                            return result
                        is_serial = lambda x,y: x.startswith("serial") and y.startswith("integer")
                        strip_serial = lambda x: x.lstrip("serial").lstrip("integer")
                        serial_logic = is_serial(x, y) or is_serial(y, x)
                        if result==False and serial_logic:
                            # use alternate serial logic
                            result = strip_serial(x)==strip_serial(y)
                        return result
                    db_field_type = getattr(models, db_field_reverse_type)(**kwargs).db_type()
                    model_type = field.db_type()
		    
                    # remove mysql's auto_increment keyword
                    if self.is_mysql and model_type.endswith("AUTO_INCREMENT"):
                        model_type = model_type.rsplit(' ', 1)[0].strip()
		    
                    # check if we can for constraints (only enabled on postgresql atm)
                    if self.is_pgsql:
                        if _constraints==None:
                            sql = """
                            SELECT
                                pg_constraint.conname, pg_get_constraintdef(pg_constraint.oid)
                            FROM
                                pg_constraint, pg_attribute
                            WHERE
                                pg_constraint.conrelid = pg_attribute.attrelid
                                AND pg_attribute.attnum = any(pg_constraint.conkey)
                                AND pg_constraint.conname ~ %s"""
                            cursor.execute(sql, [table_name])
                            _constraints = [r for r in cursor.fetchall() if r[0].endswith("_check")]
                        for r_name, r_check in _constraints:
                            if table_name+"_"+att_name==r_name.rsplit("_check")[0]:
                                r_check = r_check.replace("((", "(").replace("))", ")")
                                pos = r_check.find("(")
                                r_check = "%s\"%s" % (r_check[:pos+1], r_check[pos+1:])
                                pos = pos+r_check[pos:].find(" ")
                                r_check = "%s\" %s" % (r_check[:pos], r_check[pos+1:])
                                db_field_type += " "+r_check
                    else:
                        # remove constraints
                        model_type = model_type.split("CHECK")[0].strip()
                    c_db_field_type = clean(db_field_type)
                    c_model_type = clean(model_type)

                    if self.is_sqlite and (c_db_field_type=="varchar" and c_model_type=="char"):
                        c_db_field_type = "char"
                        db_field_type = db_field_type.lstrip("var")

                    if not cmp_or_serialcmp(c_model_type, c_db_field_type):
                        diffs.append({
                            'text' : "field '%s' not of same type: db=%s, model=%s" % (att_name, c_db_field_type, c_model_type),
                            'type' : 'type',
                            'data' : (table_name, att_name, c_db_field_type, c_model_type)
                        })
                        continue
                    if not cmp_or_serialcmp(db_field_type, model_type):
                        diffs.append({
                            'text' : "field '%s' parameters differ: db=%s, model=%s" % (att_name, db_field_type, model_type),
                            'type' : 'param',
                            'data' : (table_name, att_name, db_field_type, model_type)
                        })
                        continue
                else:
                    diffs.append({
                        'text' : "field '%s' missing in model: %s" % (att_name, model_type),
                        'type' : 'missing-in-model',
                        'data' : (table_name, att_name, db_field_type, model_type)
                    })
            for field in _meta.fields:
                if field.db_index:
                    if not field.attname in table_indexes and not field.unique:
                        diffs.append({
                            'text' : "field '%s' INDEX defined in model missing in database" % (field.attname),
                        })
            if fieldmap:
                for att_name, field in fieldmap.items():
                    diffs.append({
                        'text' : "field '%s' missing in database: %s" % (att_name, field.db_type()),
                        'type' : 'missing-in-db',
                        'data' : (table_name, att_name, field.db_type())
                    })
            if diffs:
                model_diffs.append((app_model.__name__, diffs))
Example #15
0
def table_names():
    "Returns a list of all table names that exist in the database."
    from django.db import connection, get_introspection_module
    cursor = connection.cursor()
    return set(get_introspection_module().get_table_list(cursor))
Example #16
0
def inspectdb(tableOrder=[]):
    "Generator that introspects the tables in the given database name and returns a Django model, one line at a time."
    from django.db import connection, get_introspection_module
    import keyword

    introspection_module = get_introspection_module()

    #table2model = lambda table_name: table_name.title().replace('_', '')
    table2model = lambda table_name: table_name

    cursor = connection.cursor()
    yield "# This is an auto-generated customized Django model module for the IRAM-30m Archive"
    yield "# If you provide an SQL script, the models' order should be OK"
    yield "# In other case, please rearrange models' order!"
    yield "#"
    yield "# Also Many to Many tables ('*_has_*' or '*_usedin_*') are ignored and replaced with ManyToManyField. Please check it!"
    yield "#"
    yield "# You'll have to do the following manually to clean this up:"
    yield "#    * Replace any reference to model User to use django.user (e.g %s/DjangoUsers/User/g)"
    yield "#    * Fix CharField max_length: 60->20 ; 135 -> 45 ; 765 -> 255 "
    yield "#    * Replace IntegerField with BooleanField when needed "
    yield "# Feel free to rename the models, but don't rename db_table values or field names."
    yield "#"
    yield "# Also note: You'll have to insert the output of 'django-admin.py sqlcustom [appname]'"
    yield "# into your database."
    yield ''
    yield 'from django.db import models'
    yield 'from django.contrib.auth.models import User'
    yield ''

    tablesList = sortListByList( introspection_module.get_table_list(cursor), tableOrder ) 

    pattern = re.compile("_(has|usedin)_", re.IGNORECASE)    
    #remove the many2many table
    for table_name in filter( lambda i: not pattern.search(i), tablesList ):
        yield 'class %s(models.Model):' % table2model(table_name)
        try:
            relations = introspection_module.get_relations(cursor, table_name)
        except NotImplementedError:
            relations = {}
        try:
            indexes = introspection_module.get_indexes(cursor, table_name)
        except NotImplementedError:
            indexes = {}
        for i, row in enumerate(introspection_module.get_table_description(cursor, table_name)):
            att_name = row[0].lower()
            comment_notes = [] # Holds Field notes, to be displayed in a Python comment.
            extra_params = {}  # Holds Field parameters such as 'db_column'.

            if ' ' in att_name:
                extra_params['db_column'] = att_name
                att_name = att_name.replace(' ', '')
                comment_notes.append('Field renamed to remove spaces.')
            if keyword.iskeyword(att_name):
                extra_params['db_column'] = att_name
                att_name += '_field'
                comment_notes.append('Field renamed because it was a Python reserved word.')

            if i in relations:
                rel_to = relations[i][1] == table_name and "'self'" or table2model(relations[i][1])
                field_type = 'ForeignKey(%s' % rel_to
                if att_name.endswith('_id'):
                    att_name = att_name[:-3]
                else:
                    extra_params['db_column'] = att_name
            else:
                try:
                    field_type = introspection_module.DATA_TYPES_REVERSE[row[1]]
                except KeyError:
                    field_type = 'TextField'
                    comment_notes.append('This field type is a guess.')

                # This is a hook for DATA_TYPES_REVERSE to return a tuple of
                # (field_type, extra_params_dict).
                if type(field_type) is tuple:
                    field_type, new_params = field_type
                    extra_params.update(new_params)

                # Add maxlength for all CharFields.
                if field_type == 'CharField' and row[3]:
                    extra_params['max_length'] = row[3]

                if field_type == 'DecimalField':
                    extra_params['max_digits'] = row[4]
                    extra_params['decimal_places'] = row[5]

                # Add primary_key and unique, if necessary.
                column_name = extra_params.get('db_column', att_name)
                if column_name in indexes:
                    if indexes[column_name]['primary_key']:
                        extra_params['primary_key'] = True
                    elif indexes[column_name]['unique']:
                        extra_params['unique'] = True

                field_type += '('

            # Don't output 'id = meta.AutoField(primary_key=True)', because
            # that's assumed if it doesn't exist.
            if att_name == 'id' and field_type == 'AutoField(' and extra_params == {'primary_key': True}:
                continue
            
            if att_name == 'id' and field_type == 'IntegerField(' and extra_params == {'primary_key': True}:
                continue

            # Add 'null' and 'blank', if the 'null_ok' flag was present in the
            # table description.
            if row[6]: # If it's NULL...
                extra_params['blank'] = True
                if not field_type in ('TextField(', 'CharField('):
                    extra_params['null'] = True

            field_desc = '%s = models.%s' % (att_name, field_type)
            if extra_params:
                if not field_desc.endswith('('):
                    field_desc += ', '
                field_desc += ', '.join(['%s=%r' % (k, v) for k, v in extra_params.items()])
            field_desc += ')'
            if comment_notes:
                field_desc += ' # ' + ' '.join(comment_notes)
            yield '    %s' % field_desc

        pattern = re.compile( table_name + "_has_(.+)|(.+)_usedin_"+table_name ,re.IGNORECASE )        
        # filter gets all the table with many2many names
        tablesRelatedTo = filter(pattern.search, tablesList)

        if tablesRelatedTo:            
            # and map transforms that list to a list with matches
            matchesRelatedTo = map(pattern.findall, tablesRelatedTo)
            for table,i in zip(matchesRelatedTo, range(len(matchesRelatedTo))):
                comment = "Automatically generated!"
                yield '    %s = models.ManyToManyField(db_table = "%s") # %s' % (table[0][0].lower() or table[0][1].lower(), tablesRelatedTo[i], comment)

        yield '    class Meta:'
        yield '        db_table = %r' % table_name
        yield ''
Example #17
0
def sql_delete(app, style):
    "Returns a list of the DROP TABLE SQL statements for the given app."
    from django.db import connection, models, get_introspection_module
    from django.db.backends.util import truncate_name
    from django.contrib.contenttypes import generic
    introspection = get_introspection_module()

    # This should work even if a connection isn't available
    try:
        cursor = connection.cursor()
    except:
        cursor = None

    # Figure out which tables already exist
    if cursor:
        table_names = introspection.get_table_list(cursor)
    else:
        table_names = []
    if connection.features.uses_case_insensitive_names:
        table_name_converter = lambda x: x.upper()
    else:
        table_name_converter = lambda x: x

    output = []
    qn = connection.ops.quote_name

    # Output DROP TABLE statements for standard application tables.
    to_delete = set()

    references_to_delete = {}
    app_models = models.get_models(app)
    for model in app_models:
        if cursor and table_name_converter(
                model._meta.db_table) in table_names:
            # The table exists, so it needs to be dropped
            opts = model._meta
            for f in opts.local_fields:
                if f.rel and f.rel.to not in to_delete:
                    references_to_delete.setdefault(f.rel.to, []).append(
                        (model, f))

            to_delete.add(model)

    for model in app_models:
        if cursor and table_name_converter(
                model._meta.db_table) in table_names:
            # Drop the table now
            output.append('%s %s;' %
                          (style.SQL_KEYWORD('DROP TABLE'),
                           style.SQL_TABLE(qn(model._meta.db_table))))
            if connection.features.supports_constraints and model in references_to_delete:
                for rel_class, f in references_to_delete[model]:
                    table = rel_class._meta.db_table
                    col = f.column
                    r_table = model._meta.db_table
                    r_col = model._meta.get_field(f.rel.field_name).column
                    r_name = '%s_refs_%s_%x' % (col, r_col,
                                                abs(hash((table, r_table))))
                    output.append('%s %s %s %s;' % \
                        (style.SQL_KEYWORD('ALTER TABLE'),
                        style.SQL_TABLE(qn(table)),
                        style.SQL_KEYWORD(connection.ops.drop_foreignkey_sql()),
                        style.SQL_FIELD(truncate_name(r_name, connection.ops.max_name_length()))))
                del references_to_delete[model]
            if model._meta.has_auto_field:
                ds = connection.ops.drop_sequence_sql(model._meta.db_table)
                if ds:
                    output.append(ds)

    # Output DROP TABLE statements for many-to-many tables.
    for model in app_models:
        opts = model._meta
        for f in opts.local_many_to_many:
            if isinstance(f.rel, generic.GenericRel):
                continue
            if cursor and table_name_converter(
                    f.m2m_db_table()) in table_names:
                output.append("%s %s;" %
                              (style.SQL_KEYWORD('DROP TABLE'),
                               style.SQL_TABLE(qn(f.m2m_db_table()))))
                ds = connection.ops.drop_sequence_sql(
                    "%s_%s" % (model._meta.db_table, f.column))
                if ds:
                    output.append(ds)

    app_label = app_models[0]._meta.app_label

    # Close database connection explicitly, in case this output is being piped
    # directly into a database client, to avoid locking issues.
    if cursor:
        cursor.close()
        connection.close()

    return output[::-1]  # Reverse it, to deal with table dependencies.
Example #18
0
    def handle_diff(self, app, **options):
        from django.db import models, connection, get_introspection_module
        from django.core.management import sql as _sql
        
        app_name = app.__name__.split('.')[-2]
        
        try:
            django_tables = _sql.django_table_names(only_existing=options.get('only_existing', True))
        except AttributeError:
            # backwards compatibility for before svn r7568 
            django_tables = _sql.django_table_list(only_existing=options.get('only_existing', True))
        django_tables = [django_table for django_table in django_tables if django_table.startswith(app_name)]
        
        app_models = models.get_models(app)
        if not app_models:
            return
        
        introspection_module = get_introspection_module()
        cursor = connection.cursor()
        model_diffs = []
        for app_model in app_models:
            _constraints = None
            _meta = app_model._meta
            table_name = _meta.db_table
            
            table_indexes = introspection_module.get_indexes(cursor, table_name)

            
            fieldmap = dict([(field.get_attname(), field) for field in _meta.fields])
            try:
                table_description = introspection_module.get_table_description(cursor, table_name)
            except Exception, e:
                model_diffs.append((app_model.__name__, [str(e).strip()]))
                transaction.rollback() # reset transaction
                continue
            diffs = []
            for i, row in enumerate(table_description):
                att_name = row[0].lower()
                db_field_reverse_type = introspection_module.DATA_TYPES_REVERSE.get(row[1])
                kwargs = {}
                if row[3]:
                    kwargs['max_length'] = row[3]
                if row[4]:
                    kwargs['max_digits'] = row[4]
                if row[5]:
                    kwargs['decimal_places'] = row[5]
                if row[6]:
                    kwargs['blank'] = True
                    if not db_field_reverse_type in ('TextField', 'CharField'):
                        extra_params['null'] = True
                if fieldmap.has_key(att_name):
                    field = fieldmap.pop(att_name)
                    # check type
                    def clean(s):
                        s = s.split(" ")[0]
                        s = s.split("(")[0]
                        return s
                    def cmp_or_serialcmp(x, y):
                        result = x==y
                        if result:
                            return result
                        is_serial = lambda x,y: x.startswith("serial") and y.startswith("integer")
                        strip_serial = lambda x: x.lstrip("serial").lstrip("integer")
                        serial_logic = is_serial(x, y) or is_serial(y, x)
                        if result==False and serial_logic:
                            # use alternate serial logic
                            result = strip_serial(x)==strip_serial(y)
                        return result
                    db_field_type = getattr(models, db_field_reverse_type)(**kwargs).db_type()
                    model_type = field.db_type()
                    # check if we can for constraints (only enabled on postgresql atm)
                    if self.is_pgsql:
                        if _constraints==None:
                            sql = """
                            SELECT
                                pg_constraint.conname, pg_get_constraintdef(pg_constraint.oid)
                            FROM
                                pg_constraint, pg_attribute
                            WHERE
                                pg_constraint.conrelid = pg_attribute.attrelid
                                AND pg_attribute.attnum = any(pg_constraint.conkey)
                                AND pg_constraint.conname ~ %s"""
                            cursor.execute(sql, [table_name])
                            _constraints = [r for r in cursor.fetchall() if r[0].endswith("_check")]
                        for r_name, r_check in _constraints:
                            if table_name+"_"+att_name==r_name.rsplit("_check")[0]:
                                r_check = r_check.replace("((", "(").replace("))", ")")
                                pos = r_check.find("(")
                                r_check = "%s\"%s" % (r_check[:pos+1], r_check[pos+1:])
                                pos = pos+r_check[pos:].find(" ")
                                r_check = "%s\" %s" % (r_check[:pos], r_check[pos+1:])
                                db_field_type += " "+r_check
                    else:
                        # remove constraints
                        model_type = model_type.split("CHECK")[0].strip()
                    c_db_field_type = clean(db_field_type)
                    c_model_type = clean(model_type)
                    if not cmp_or_serialcmp(c_model_type, c_db_field_type):
                        diffs.append("field '%s' not of same type: db=%s, model=%s" % (att_name, c_db_field_type, c_model_type))
                        continue
                    if not cmp_or_serialcmp(db_field_type, model_type):
                        diffs.append("field '%s' parameters differ: db=%s, model=%s" % (att_name, db_field_type, model_type))
                        continue
                else:
                    diffs.append("field '%s' missing in model field" % att_name)
            for field in _meta.fields:
                if field.db_index:
                    if not field.attname in table_indexes and not field.unique:
                        diffs.append("field '%s' INDEX defined in model missing in database" % (field.attname))
            if fieldmap:
                for att_name, field in fieldmap.items():
                    diffs.append("field '%s' missing in database" % att_name)
            if diffs:
                model_diffs.append((app_model.__name__, diffs))