Exemplo n.º 1
0
    def change_meta_unique_together(self, model, old_unique_together,
                                    new_unique_together):
        """Changes the unique_together constraints of a table."""
        sql_result = SQLResult()

        old_unique_together = set(old_unique_together)
        new_unique_together = set(new_unique_together)

        to_remove = old_unique_together.difference(new_unique_together)

        for field_names in to_remove:
            fields = self.get_fields_for_names(model, field_names)
            columns = self.get_column_names_for_fields(fields)
            index_name = self.find_index_name(model, columns, unique=True)

            if index_name:
                self.remove_recorded_index(model, index_name, unique=True)
                sql_result.add_sql(
                    self.get_drop_unique_constraint_sql(model, index_name))

        for field_names in new_unique_together:
            fields = self.get_fields_for_names(model, field_names)
            columns = self.get_column_names_for_fields(fields)
            index_name = self.find_index_name(model, columns, unique=True)

            if not index_name:
                # This doesn't exist in the database, so we want to add it.
                index_name = self.get_new_index_name(model, fields,
                                                     unique=True)
                sql_result.add_sql(
                    self.create_unique_index(model, index_name, fields))

        return sql_result
Exemplo n.º 2
0
    def mutate(self, mutator, model):
        sql_result = SQLResult()

        # Remove any many to many tables.
        for field_name, field_sig in mutator.model_sig['fields'].items():
            if field_sig['field_type'] is models.ManyToManyField:
                field = model._meta.get_field(field_name)
                m2m_table = field._get_m2m_db_table(model._meta)
                sql_result.add(mutator.evolver.delete_table(m2m_table))

        # Remove the table itself.
        sql_result.add(mutator.evolver.delete_table(model._meta.db_table))

        mutator.add_sql(self, sql_result)
Exemplo n.º 3
0
    def create_index(self, model, field):
        """Returns the SQL for creating an index for a single field.

        The index will be recorded in the database signature for future
        operations within the transaction, and the appropriate SQL for
        creating the index will be returned.

        This is not intended to be overridden.
        """
        table_name = model._meta.db_table
        column = field.column
        index_state = self.database_state.find_index(table_name=table_name,
                                                     columns=[column])

        if index_state:
            return []

        self.database_state.add_index(table_name=table_name,
                                      index_name=create_index_name(
                                          self.connection,
                                          table_name,
                                          field_names=[field.name],
                                          col_names=[column]),
                                      columns=[column])

        return SQLResult(sql_indexes_for_field(self.connection, model, field))
Exemplo n.º 4
0
    def get_drop_index_sql(self, model, index_name):
        qn = self.connection.ops.quote_name

        return SQLResult([
            'DROP INDEX %s ON %s;'
            % (qn(index_name), qn(model._meta.db_table))
        ])
Exemplo n.º 5
0
    def rename_table(self, model, old_db_tablename, db_tablename):
        sql_result = SQLResult()

        if old_db_tablename == db_tablename:
            # No Operation
            return sql_result

        max_name_length = self.connection.ops.max_name_length()

        refs = {}
        models = []

        for field in model._meta.local_many_to_many:
            remote_field = get_remote_field(field)

            if (remote_field and remote_field.through and
                    remote_field.through._meta.db_table == old_db_tablename):

                through = remote_field.through

                for m2m_field in through._meta.local_fields:
                    remote_m2m_field = get_remote_field(m2m_field)

                    if remote_m2m_field:
                        remote_m2m_field_model = get_remote_field_model(
                            remote_m2m_field)

                        if remote_m2m_field_model == model:
                            models.append(remote_m2m_field_model)
                            refs.setdefault(remote_m2m_field_model, []).append(
                                (through, m2m_field))

        remove_refs = refs.copy()

        if self.supports_constraints:
            for relto in models:
                sql_result.add_pre_sql(
                    sql_delete_constraints(self.connection, relto,
                                           remove_refs))

        sql_result.add(
            self.get_rename_table_sql(model, old_db_tablename, db_tablename))

        for relto in models:
            for rel_class, f in refs[relto]:
                if rel_class._meta.db_table == old_db_tablename:
                    rel_class._meta.db_table = db_tablename

                rel_class._meta.db_table = \
                    truncate_name(rel_class._meta.db_table,
                                  max_name_length)

            if self.supports_constraints:
                sql_result.add_post_sql(
                    sql_add_constraints(self.connection, relto, refs))

        return sql_result
Exemplo n.º 6
0
    def get_drop_index_sql(self, model, index_name):
        """Returns the database-specific SQL to drop an index.

        This can be overridden by subclasses if they use a syntax
        other than "DROP INDEX <name>;"
        """
        qn = self.connection.ops.quote_name

        return SQLResult(['DROP INDEX %s;' % qn(index_name)])
Exemplo n.º 7
0
    def create_unique_index(self, model, index_name, fields):
        qn = self.connection.ops.quote_name

        self.record_index(model, fields, index_name=index_name, unique=True)

        return SQLResult([
            'CREATE UNIQUE INDEX %s ON %s (%s);' %
            (index_name, model._meta.db_table, ', '.join(
                [qn(field.column) for field in fields])),
        ])
Exemplo n.º 8
0
    def get_rename_table_sql(self, model, old_db_tablename, db_tablename):
        qn = self.connection.ops.quote_name

        # We want to define an explicit ALTER TABLE here, instead of setting
        # alter_table in AlterTableSQLResult, so that we can be explicit about
        # the old and new table names.
        return SQLResult([
            'ALTER TABLE %s RENAME TO %s;' %
            (qn(old_db_tablename), qn(db_tablename))
        ])
Exemplo n.º 9
0
    def mutate(self, mutator, model):
        """Schedule a model deletion on the mutator.

        This will instruct the mutator to delete a model. It will be scheduled
        and later executed on the database, if not optimized out.

        Args:
            mutator (django_evolution.mutators.ModelMutator):
                The mutator to perform an operation on.

            model (MockModel):
                The model being mutated.
        """
        sql_result = SQLResult()

        # Remove any many-to-many tables.
        for field_sig in mutator.model_sig.field_sigs:
            if issubclass(field_sig.field_type, models.ManyToManyField):
                field = model._meta.get_field(field_sig.field_name)
                m2m_table = field._get_m2m_db_table(model._meta)
                sql_result.add(mutator.evolver.delete_table(m2m_table))

        # Remove the table itself.
        sql_result.add(mutator.evolver.delete_table(model._meta.db_table))

        mutator.add_sql(self, sql_result)
Exemplo n.º 10
0
    def change_meta_unique_together(self, model, old_unique_together,
                                    new_unique_together):
        """Changes the unique_together constraints of a table."""
        sql_result = SQLResult()

        old_unique_together = set(old_unique_together)
        new_unique_together = set(new_unique_together)

        to_remove = old_unique_together.difference(new_unique_together)

        for field_names in to_remove:
            fields = self.get_fields_for_names(model, field_names)
            columns = self.get_column_names_for_fields(fields)
            index_name = self.find_index_name(model, columns, unique=True)

            if index_name:
                self.remove_recorded_index(model, index_name, unique=True)
                sql_result.add_sql(
                    self.get_drop_unique_constraint_sql(model, index_name))

        for field_names in new_unique_together:
            fields = self.get_fields_for_names(model, field_names)
            columns = self.get_column_names_for_fields(fields)
            index_name = self.find_index_name(model, columns, unique=True)

            if not index_name:
                # This doesn't exist in the database, so we want to add it.
                index_name = self.get_new_index_name(model,
                                                     fields,
                                                     unique=True)
                sql_result.add_sql(
                    self.create_unique_index(model, index_name, fields))

        return sql_result
Exemplo n.º 11
0
    def change_meta_unique_together(self, model, old_unique_together,
                                    new_unique_together):
        """Change the unique_together constraints of a table.

        Args:
            model (django.db.models.Model):
                The model being changed.

            old_unique_together (list):
                The old value for ``unique_together``.

            new_unique_together (list):
                The new value for ``unique_together``.

        Returns:
            django_evolution.sql_result.SQLResult:
            The SQL statements for changing the ``unique_together``
            constraints.
        """
        sql_result = SQLResult()
        table_name = model._meta.db_table

        old_unique_together = set(old_unique_together)
        new_unique_together = set(new_unique_together)

        to_remove = old_unique_together.difference(new_unique_together)

        for field_names in to_remove:
            fields = self.get_fields_for_names(model, field_names)
            index_state = self.database_state.find_index(
                table_name=table_name,
                columns=self.get_column_names_for_fields(fields),
                unique=True)

            if index_state:
                index_name = index_state.name

                self.database_state.remove_index(table_name=table_name,
                                                 index_name=index_name,
                                                 unique=True)
                sql_result.add_sql(
                    self.get_drop_unique_constraint_sql(model, index_name))

        for field_names in new_unique_together:
            fields = self.get_fields_for_names(model, field_names)
            index_state = self.database_state.find_index(
                table_name=table_name,
                columns=self.get_column_names_for_fields(fields),
                unique=True)

            if not index_state:
                # This doesn't exist in the database, so we want to add it.
                index_name = self.get_new_index_name(model,
                                                     fields,
                                                     unique=True)
                sql_result.add_sql(
                    self.create_unique_index(model, index_name, fields))

        return sql_result
Exemplo n.º 12
0
    def get_change_unique_sql(self, model, field, new_unique_value,
                              constraint_name, initial):
        qn = self.connection.ops.quote_name
        opts = model._meta
        sql = []

        if new_unique_value:
            sql.append('CREATE UNIQUE INDEX %s ON %s(%s);' %
                       (constraint_name, qn(opts.db_table), qn(field.column)))
        else:
            sql.append('DROP INDEX %s ON %s;' %
                       (constraint_name, qn(opts.db_table)))

        return SQLResult(sql)
Exemplo n.º 13
0
    def create_unique_index(self, model, index_name, fields):
        qn = self.connection.ops.quote_name
        table_name = model._meta.db_table

        self.database_state.add_index(
            table_name=table_name,
            index_name=index_name,
            columns=self.get_column_names_for_fields(fields),
            unique=True)

        return SQLResult([
            'CREATE UNIQUE INDEX %s ON %s (%s);' %
            (qn(index_name), qn(table_name), ', '.join(
                [qn(field.column) for field in fields])),
        ])
Exemplo n.º 14
0
    def change_meta_index_together(self, model, old_index_together,
                                   new_index_together):
        """Change the index_together indexes of a table.

        Args:
            model (django.db.models.Model):
                The model being changed.

            old_index_together (list):
                The old value for ``index_together``.

            new_index_together (list):
                The new value for ``index_together``.

        Returns:
            django_evolution.sql_result.SQLResult:
            The SQL statements for changing the ``index_together`` indexes.
        """
        sql_result = SQLResult()
        table_name = model._meta.db_table

        old_index_together = set(old_index_together or [])
        new_index_together = set(new_index_together)

        to_remove = old_index_together.difference(new_index_together)

        for field_names in to_remove:
            fields = self.get_fields_for_names(model, field_names)
            index_state = self.database_state.find_index(
                table_name=table_name,
                columns=self.get_column_names_for_fields(fields))

            if index_state:
                sql_result.add(self.drop_index_by_name(model,
                                                       index_state.name))

        for field_names in new_index_together:
            fields = self.get_fields_for_names(model, field_names)
            columns = self.get_column_names_for_fields(fields)
            index_state = self.database_state.find_index(table_name=table_name,
                                                         columns=columns)

            if not index_state:
                # This doesn't exist in the database, so we want to add it.
                index_name = self.get_default_index_together_name(
                    table_name, fields)
                self.database_state.add_index(table_name=table_name,
                                              index_name=index_name,
                                              columns=columns)
                sql_result.add(
                    sql_indexes_for_fields(self.connection,
                                           model,
                                           fields,
                                           index_together=True))

        return sql_result
Exemplo n.º 15
0
    def rename_table(self, model, old_db_tablename, db_tablename):
        sql_result = SQLResult()

        if old_db_tablename == db_tablename:
            # No Operation
            return sql_result

        max_name_length = self.connection.ops.max_name_length()

        refs = {}
        models = []

        for field in model._meta.local_many_to_many:
            if (field.rel and
                field.rel.through and
                field.rel.through._meta.db_table == old_db_tablename):

                through = field.rel.through

                for m2m_field in through._meta.local_fields:
                    if m2m_field.rel and m2m_field.rel.to == model:
                        models.append(m2m_field.rel.to)
                        refs.setdefault(m2m_field.rel.to, []).append(
                            (through, m2m_field))

        remove_refs = refs.copy()

        if self.supports_constraints:
            for relto in models:
                sql_result.add_pre_sql(sql_delete_constraints(
                    self.connection, relto, remove_refs))

        sql_result.add(self.get_rename_table_sql(
            model, old_db_tablename, db_tablename))

        for relto in models:
            for rel_class, f in refs[relto]:
                if rel_class._meta.db_table == old_db_tablename:
                    rel_class._meta.db_table = db_tablename

                rel_class._meta.db_table = \
                    truncate_name(rel_class._meta.db_table,
                                  max_name_length)

            if self.supports_constraints:
                sql_result.add_post_sql(sql_add_constraints(
                    self.connection, relto, refs))

        return sql_result
Exemplo n.º 16
0
    def create_index(self, model, f):
        """Returns the SQL for creating an index for a single field.

        The index will be recorded in the database signature for future
        operations within the transaction, and the appropriate SQL for
        creating the index will be returned.

        This is not intended to be overridden.
        """
        index_name = self.find_index_name(model, [f.column])

        if index_name:
            return []

        self.record_index(model, [f])

        return SQLResult(sql_indexes_for_field(self.connection, model, f))
Exemplo n.º 17
0
    def mutate(self, mutator, model):
        sql_result = SQLResult()

        # Remove any many to many tables.
        for field_name, field_sig in mutator.model_sig['fields'].items():
            if field_sig['field_type'] is models.ManyToManyField:
                field = model._meta.get_field(field_name)
                m2m_table = field._get_m2m_db_table(model._meta)
                sql_result.add(mutator.evolver.delete_table(m2m_table))

        # Remove the table itself.
        sql_result.add(mutator.evolver.delete_table(model._meta.db_table))

        mutator.add_sql(self, sql_result)
Exemplo n.º 18
0
    def get_drop_index_sql(self, model, index_name):
        """Returns the database-specific SQL to drop an index.

        This can be overridden by subclasses if they use a syntax
        other than "DROP INDEX <name>;"
        """
        qn = self.connection.ops.quote_name

        if hasattr(self.connection, 'SchemaEditorClass'):
            # Django >= 1.7
            delete_index_sql = (
                self.connection.SchemaEditorClass.sql_delete_index % {
                    'name': qn(index_name),
                    'table': qn(model._meta.db_table),
                })
        else:
            # Django < 1.7
            delete_index_sql = 'DROP INDEX %s' % qn(index_name)

        return SQLResult(['%s;' % delete_index_sql])
Exemplo n.º 19
0
    def get_rename_table_sql(self, model, old_db_table, new_db_table):
        """Return SQL for renaming a table.

        Args:
            model (django.db.models.Model):
                The model representing the table to rename.

            old_db_table (unicode):
                The old table name.

            new_db_table (unicode):
                The new table name.

        Returns:
            django_evolution.db.sql_result.SQLResult:
            The resulting SQL for renaming the table.
        """
        qn = self.connection.ops.quote_name

        return SQLResult([
            'RENAME TABLE %s TO %s;'
            % (qn(old_db_table), qn(new_db_table))
        ])
Exemplo n.º 20
0
    def get_rename_table_sql(self, model, old_db_tablename, db_tablename):
        qn = self.connection.ops.quote_name

        return SQLResult([
            'RENAME TABLE %s TO %s;' % (qn(old_db_tablename), qn(db_tablename))
        ])
Exemplo n.º 21
0
 def delete_table(self, table_name):
     qn = self.connection.ops.quote_name
     return SQLResult(['DROP TABLE %s;' % qn(table_name)])
Exemplo n.º 22
0
    def change_meta_indexes(self, model, old_indexes, new_indexes):
        """Change the indexes of a table defined in a model's indexes list.

        This will apply a set of indexes serialized from a
        :py:attr:`Meta.indexes <django.db.models.options.Options.indexes>`
        to the database. The serialized values are those passed to
        :py:class:`~django_evolution.mutations.ChangeMeta`, in the form of::

            [
                {
                    'name': 'optional-index-name',
                    'fields': ['field1', '-field2_sorted_desc'],
                },
                ...
            ]

        Args:
            model (django.db.models.Model):
                The model being changed.

            old_indexes (list):
                The old serialized value for the indexes.

            new_indexes (list):
                The new serialized value for the indexes.

        Returns:
            django_evolution.sql_result.SQLResult:
            The SQL statements for changing the indexes.
        """
        # The mutation should have failed before getting here on older
        # versions of Django.
        assert django.VERSION >= (1, 11)

        if not old_indexes:
            old_indexes = []

        # We're working with dictionaries and lists, which we can't just pass
        # into set() like we would for the other methods. We need to calculate
        # an explicit, ordered list of indexes, so to do this, we're going to
        # build a set of tuples representing the old values and the new
        # values, and then calculate an ordered list of indexes to remove and
        # to add based on values found in those sets.
        def _make_index_tuple(index_info):
            return (index_info.get('name'), tuple(index_info['fields']))

        old_indexes_set = set(
            _make_index_tuple(index_info) for index_info in old_indexes)

        new_indexes_set = set(
            _make_index_tuple(index_info) for index_info in new_indexes)

        to_remove = [
            index_info for index_info in old_indexes
            if _make_index_tuple(index_info) not in new_indexes_set
        ]

        to_add = [
            index_info for index_info in new_indexes
            if _make_index_tuple(index_info) not in old_indexes_set
        ]

        sql_result = SQLResult()
        table_name = model._meta.db_table

        with self.connection.schema_editor(collect_sql=True) as schema_editor:
            for index_info in to_remove:
                index_field_names = index_info['fields']
                index_name = index_info.get('name')

                if index_name:
                    index_state = self.database_state.get_index(
                        table_name=table_name, index_name=index_name)
                else:
                    # No explicit index name was given, so see if we can find
                    # one that matches in the database.
                    fields = self.get_fields_for_names(
                        model, index_field_names, allow_sort_prefixes=True)
                    index_state = self.database_state.find_index(
                        table_name=table_name,
                        columns=self.get_column_names_for_fields(fields))

                if index_state:
                    # We found a suitable index name, and a matching index
                    # entry in the database. Remove it.
                    index = models.Index(fields=list(index_field_names),
                                         name=index_state.name)
                    sql_result.add('%s;' %
                                   index.remove_sql(model, schema_editor))

            for index_info in to_add:
                index_field_names = index_info['fields']
                index_name = index_info.get('name')
                fields = self.get_fields_for_names(model,
                                                   index_field_names,
                                                   allow_sort_prefixes=True)

                if index_name:
                    index_state = self.database_state.get_index(
                        table_name=table_name, index_name=index_name)
                else:
                    # No explicit index name was given, so see if we can find
                    # one that matches in the database.
                    index_state = self.database_state.find_index(
                        table_name=table_name,
                        columns=self.get_column_names_for_fields(fields))

                    if index_state:
                        index_name = index_state.name

                if not index_name or not index_state:
                    # This is a new index not found in the database. We can
                    # record it and proceed.
                    index = models.Index(fields=list(index_field_names),
                                         name=index_name)

                    if not index_name:
                        index.set_name_with_model(model)

                    self.database_state.add_index(
                        table_name=table_name,
                        index_name=index.name,
                        columns=self.get_column_names_for_fields(fields))
                    sql_result.add('%s;' %
                                   index.create_sql(model, schema_editor))

        return sql_result