Пример #1
0
    def rename_column(self, model, old_field, new_field):
        if old_field.column == new_field.column:
            # No Operation
            return []

        qn = self.connection.ops.quote_name
        max_name_length = self.connection.ops.max_name_length()

        sql_result = AlterTableSQLResult(self, model)

        pre_sql, stash = self.stash_field_ref_constraints(model=model,
                                                          replaced_fields={
                                                              old_field:
                                                              new_field,
                                                          })
        sql_result.add_pre_sql(pre_sql)

        sql_result.add_alter_table([{
            'independent':
            True,
            'sql':
            'RENAME COLUMN %s TO %s' %
            (truncate_name(qn(old_field.column), max_name_length),
             truncate_name(qn(new_field.column), max_name_length)),
        }])

        sql_result.add_post_sql(self.restore_field_ref_constraints(stash))

        return sql_result
Пример #2
0
    def add_column(self, model, f, initial):
        qn = self.connection.ops.quote_name
        sql_result = AlterTableSQLResult(self, model)

        if f.rel:
            # it is a foreign key field
            # NOT NULL REFERENCES "django_evolution_addbasemodel"
            # ("id") DEFERRABLE INITIALLY DEFERRED

            # ALTER TABLE <tablename> ADD COLUMN <column name> NULL
            # REFERENCES <tablename1> ("<colname>") DEFERRABLE INITIALLY
            # DEFERRED
            related_model = f.rel.to
            related_table = related_model._meta.db_table
            related_pk_col = related_model._meta.pk.name
            constraints = ['%sNULL' % (not f.null and 'NOT ' or '')]

            if f.unique or f.primary_key:
                constraints.append('UNIQUE')

            sql_result.add_alter_table([
                {
                    'op': 'ADD COLUMN',
                    'column': f.column,
                    'db_type': f.db_type(connection=self.connection),
                    'params': constraints + [
                        'REFERENCES',
                        qn(related_table),
                        '(%s)' % qn(related_pk_col),
                        self.connection.ops.deferrable_sql(),
                    ]
                }
            ])
        else:
            null_constraints = '%sNULL' % (not f.null and 'NOT ' or '')

            if f.unique or f.primary_key:
                unique_constraints = 'UNIQUE'
            else:
                unique_constraints = ''

            # At this point, initial can only be None if null=True,
            # otherwise it is a user callable or the default
            # AddFieldInitialCallback which will shortly raise an exception.
            if initial is not None:
                if callable(initial):
                    sql_result.add_alter_table([
                        {
                            'op': 'ADD COLUMN',
                            'column': f.column,
                            'db_type': f.db_type(connection=self.connection),
                            'params': [unique_constraints],
                        }
                    ])

                    sql_result.add_sql([
                        'UPDATE %s SET %s = %s WHERE %s IS NULL;'
                        % (qn(model._meta.db_table), qn(f.column),
                           initial(), qn(f.column))
                    ])

                    if not f.null:
                        # Only put this sql statement if the column cannot
                        # be null.
                        sql_result.add_sql(
                            self.set_field_null(model, f, f.null))
                else:
                    sql_result.add_alter_table([
                        {
                            'op': 'ADD COLUMN',
                            'column': f.column,
                            'db_type': f.db_type(connection=self.connection),
                            'params': [
                                null_constraints,
                                unique_constraints,
                                'DEFAULT',
                                '%s',
                            ],
                            'sql_params': [initial]
                        }
                    ])

                    # Django doesn't generate default columns, so now that
                    # we've added one to get default values for existing
                    # tables, drop that default.
                    sql_result.add_post_sql([
                        'ALTER TABLE %s ALTER COLUMN %s DROP DEFAULT;'
                        % (qn(model._meta.db_table), qn(f.column))
                    ])
            else:
                sql_result.add_alter_table([
                    {
                        'op': 'ADD COLUMN',
                        'column': f.column,
                        'db_type': f.db_type(connection=self.connection),
                        'params': [null_constraints, unique_constraints],
                    }
                ])

        if f.unique or f.primary_key:
            self.record_index(model, [f], use_constraint_name=True,
                              unique=True)

        return sql_result
Пример #3
0
    def get_change_column_type_sql(self, model, old_field, new_field):
        """Return SQL to change the type of a column.

        Version Added:
            2.2

        Args:
            model (type):
                The type of model owning the field.

            old_field (django.db.models.Field):
                The old field.

            new_field (django.db.models.Field):
                The new field.

        Returns:
            django_evolution.sql_result.AlterTableSQLResult:
            The SQL statements for changing the column type.
        """
        connection = self.connection
        qn = connection.ops.quote_name

        schema = self.build_column_schema(
            model=model,
            field=new_field,
            initial=new_field.default,
            skip_null_constraint=True,
            skip_primary_or_unique_constraint=True,
            skip_references=True)
        column_name = schema['name']
        table_name = model._meta.db_table

        sql_result = AlterTableSQLResult(self, model)

        old_field_type = old_field.db_type(connection=connection).lower()
        new_field_type = schema['db_type'].lower()

        was_serial = old_field_type in self.alter_field_type_map
        is_serial = new_field_type in self.alter_field_type_map

        if is_serial:
            # This is a serial field. We will need to change the type and
            # update the sequence. We will also need to choose the actual
            # type to set for the column definition.
            new_field_type = self.alter_field_type_map.get(
                new_field_type, new_field_type)

        alter_type_params = ['TYPE', new_field_type] + schema['definition']

        if not self._are_column_types_compatible(old_field, new_field):
            alter_type_params += [
                'USING',
                '%s::%s' % (column_name, new_field_type),
            ]

        sql_result.add_alter_table([{
            'op':
            'ALTER COLUMN',
            'column':
            column_name,
            'params':
            alter_type_params,
            'sql_params':
            schema['definition_sql_params'],
        }])

        if is_serial:
            # Reset the sequence.
            sequence_name = '%s_%s_seq' % (table_name, column_name)

            sequence_sql_result = AlterTableSQLResult(self, model)
            sequence_sql_result.add_pre_sql([
                'DROP SEQUENCE IF EXISTS %s CASCADE;' % qn(sequence_name),
                'CREATE SEQUENCE %s;' % qn(sequence_name),
            ])
            sequence_sql_result.add_alter_table([{
                'op':
                'ALTER COLUMN',
                'column':
                column_name,
                'params': [
                    'SET',
                    'DEFAULT',
                    "nextval('%s')" % qn(sequence_name),
                ],
            }])
            sequence_sql_result.add_post_sql([
                "SELECT setval('%s', MAX(%s)) FROM %s;" %
                (qn(sequence_name), qn(column_name), qn(table_name)),
                'ALTER SEQUENCE %s OWNED BY %s.%s;' %
                (qn(sequence_name), qn(table_name), qn(column_name)),
            ])

            sql_result.add_post_sql(sequence_sql_result)
        elif was_serial:
            # Drop the old sequence, since we no longer need it.
            sequence_name = '%s_%s_seq' % (table_name, old_field.column)
            sql_result.add_post_sql([
                'DROP SEQUENCE IF EXISTS %s CASCADE;' % qn(sequence_name),
            ])

        return sql_result
Пример #4
0
    def add_column(self, model, f, initial):
        qn = self.connection.ops.quote_name
        sql_result = AlterTableSQLResult(self, model)

        if f.rel:
            # it is a foreign key field
            # NOT NULL REFERENCES "django_evolution_addbasemodel"
            # ("id") DEFERRABLE INITIALLY DEFERRED

            # ALTER TABLE <tablename> ADD COLUMN <column name> NULL
            # REFERENCES <tablename1> ("<colname>") DEFERRABLE INITIALLY
            # DEFERRED
            related_model = f.rel.to
            related_table = related_model._meta.db_table
            related_pk_col = related_model._meta.pk.name
            constraints = ['%sNULL' % (not f.null and 'NOT ' or '')]

            if f.unique or f.primary_key:
                constraints.append('UNIQUE')

            sql_result.add_alter_table([{
                'op':
                'ADD COLUMN',
                'column':
                f.column,
                'db_type':
                f.db_type(connection=self.connection),
                'params':
                constraints + [
                    'REFERENCES',
                    qn(related_table),
                    '(%s)' % qn(related_pk_col),
                    self.connection.ops.deferrable_sql(),
                ]
            }])
        else:
            null_constraints = '%sNULL' % (not f.null and 'NOT ' or '')

            if f.unique or f.primary_key:
                unique_constraints = 'UNIQUE'
            else:
                unique_constraints = ''

            # At this point, initial can only be None if null=True,
            # otherwise it is a user callable or the default
            # AddFieldInitialCallback which will shortly raise an exception.
            if initial is not None:
                if callable(initial):
                    sql_result.add_alter_table([{
                        'op':
                        'ADD COLUMN',
                        'column':
                        f.column,
                        'db_type':
                        f.db_type(connection=self.connection),
                        'params': [unique_constraints],
                    }])

                    sql_result.add_sql([
                        'UPDATE %s SET %s = %s WHERE %s IS NULL;' %
                        (qn(model._meta.db_table), qn(
                            f.column), initial(), qn(f.column))
                    ])

                    if not f.null:
                        # Only put this sql statement if the column cannot
                        # be null.
                        sql_result.add_sql(
                            self.set_field_null(model, f, f.null))
                else:
                    sql_result.add_alter_table([{
                        'op':
                        'ADD COLUMN',
                        'column':
                        f.column,
                        'db_type':
                        f.db_type(connection=self.connection),
                        'params': [
                            null_constraints,
                            unique_constraints,
                            'DEFAULT',
                            '%s',
                        ],
                        'sql_params': [initial]
                    }])

                    # Django doesn't generate default columns, so now that
                    # we've added one to get default values for existing
                    # tables, drop that default.
                    sql_result.add_post_sql([
                        'ALTER TABLE %s ALTER COLUMN %s DROP DEFAULT;' %
                        (qn(model._meta.db_table), qn(f.column))
                    ])
            else:
                sql_result.add_alter_table([{
                    'op':
                    'ADD COLUMN',
                    'column':
                    f.column,
                    'db_type':
                    f.db_type(connection=self.connection),
                    'params': [null_constraints, unique_constraints],
                }])

        if f.unique or f.primary_key:
            self.record_index(model, [f],
                              use_constraint_name=True,
                              unique=True)

        return sql_result
Пример #5
0
    def rename_column(self, model, old_field, new_field):
        """Rename the specified column.

        This will rename the column through ``ALTER TABLE .. CHANGE COLUMN``.

        Any constraints on the column will be stashed away before the
        ``ALTER TABLE`` and restored afterward.

        If the column has not actually changed, or it's not a real column
        (a many-to-many relation), then this will return empty statements.

        Args:
            model (type):
                The model representing the table containing the column.

            old_field (django.db.models.Field):
                The old field definition.

            new_field (django.db.models.Field):
                The new field definition.

        Returns:
            django_evolution.db.sql_result.AlterTableSQLResult or list:
            The statements for renaming the column. This may be an empty
            list if the column won't be renamed.
        """
        if old_field.column == new_field.column:
            # No Operation
            return []

        col_type = new_field.db_type(connection=self.connection)

        if col_type is None:
            # Skip ManyToManyFields, because they're not represented as
            # database columns in this table.
            return []

        qn = self.connection.ops.quote_name
        sql_result = AlterTableSQLResult(self, model)

        pre_sql, stash = self.stash_field_ref_constraints(
            model=model,
            replaced_fields={
                old_field: new_field,
            })
        sql_result.add_pre_sql(pre_sql)

        schema = self.build_column_schema(model=model,
                                          field=new_field,
                                          initial=new_field.default)

        alter_table_items = []

        if old_field.primary_key:
            alter_table_items.append('DROP PRIMARY KEY')

        alter_table_items.append(
            'CHANGE COLUMN %s %s'
            % (qn(old_field.column), ' '.join([
                qn(schema['name']),
                schema['db_type'],
            ] + schema['definition'])))

        sql_result.add_alter_table([{
            'sql': ', '.join(alter_table_items),
        }])
        sql_result.add_post_sql(self.restore_field_ref_constraints(stash))

        return sql_result