示例#1
0
def write_sql(sql, database):
    """Output and return a list of SQL statements.

    Args:
        sql (list):
            A list of SQL statements. Each entry might be a string, or a
            tuple consisting of a format string and formatting arguments.

        database (unicode):
            The database the SQL statements would be executed on.

    Returns:
        list of unicode:
        The formatted list of SQL statements.
    """
    evolver = EvolutionOperationsMulti(database).get_evolver()
    qp = evolver.quote_sql_param
    out_sql = []

    for statement in sql:
        if isinstance(statement, tuple):
            statement = six.text_type(statement[0] % tuple(
                qp(evolver.normalize_value(s))
                for s in statement[1]
            ))

        print(statement)
        out_sql.append(statement)

    return out_sql
示例#2
0
    def rescan_indexes(self):
        """Rescan the list of indexes from the database.

        This will look up all indexes found in the database, recording each
        one's table. If there are existing indexes being tracked for a table
        containing indexes, they will be removed.
        """
        evolver = EvolutionOperationsMulti(self.db_name).get_evolver()
        connection = evolver.connection
        introspection = connection.introspection
        cursor = connection.cursor()

        for table_name in introspection.get_table_list(cursor):
            if hasattr(table_name, 'name'):
                # Django >= 1.7
                table_name = table_name.name

            if self.has_table(table_name):
                self.clear_indexes(table_name)
            else:
                self.add_table(table_name)

            indexes = evolver.get_indexes_for_table(table_name)

            for index_name, index_info in six.iteritems(indexes):
                self.add_index(table_name=table_name,
                               index_name=index_name,
                               columns=index_info['columns'],
                               unique=index_info['unique'])
示例#3
0
    def rescan_tables(self):
        """Rescan the list of tables from the database.

        This will look up all tables found in the database, along with
        information (such as indexes) on those tables.

        Existing information on the tables will be flushed.
        """
        evolver = EvolutionOperationsMulti(self.db_name).get_evolver()
        connection = evolver.connection
        introspection = connection.introspection
        cursor = connection.cursor()

        for table_name in introspection.get_table_list(cursor):
            # NOTE: The table names are already normalized, so there's no
            #       need to normalize them again.
            if hasattr(table_name, 'name'):
                # In Django >= 1.7, we get back TableInfo namedtuples,
                # which have 'name' and 'type' keys. We don't care about
                # anything but 'name'.
                table_name = table_name.name

            if self.has_table(table_name):
                self.clear_indexes(table_name)
            else:
                self.add_table(table_name)

            constraints = evolver.get_constraints_for_table(table_name)

            for constraint_name, constraint_info in six.iteritems(constraints):
                self.add_index(table_name=table_name,
                               index_name=constraint_name,
                               columns=constraint_info['columns'],
                               unique=constraint_info['unique'])
示例#4
0
def execute_sql(cursor, sql, database):
    """
    Execute a list of SQL statements on the provided cursor, unrolling
    parameters as required
    """
    evolver = EvolutionOperationsMulti(database).get_evolver()
    statement = None

    try:
        for statement in sql:
            if isinstance(statement, tuple):
                statement = (statement[0].strip(), statement[1])

                if statement[0] and not statement[0].startswith('--'):
                    cursor.execute(
                        statement[0],
                        tuple(
                            evolver.normalize_value(s) for s in statement[1]))
            else:
                statement = statement.strip()

                if statement and not statement.startswith('--'):
                    cursor.execute(statement)
    except Exception, e:
        # Augment the exception so that callers can get the SQL statement
        # that failed.
        e.last_sql_statement = statement

        raise
示例#5
0
    def rescan_indexes(self):
        """Rescan the list of indexes from the database.

        This will look up all indexes found in the database, recording each
        one's table. If there are existing indexes being tracked for a table
        containing indexes, they will be removed.
        """
        evolver = EvolutionOperationsMulti(self.db_name).get_evolver()
        connection = evolver.connection
        introspection = connection.introspection
        cursor = connection.cursor()

        for table_name in introspection.get_table_list(cursor):
            if hasattr(table_name, 'name'):
                # Django >= 1.7
                table_name = table_name.name

            if self.has_table(table_name):
                self.clear_indexes(table_name)
            else:
                self.add_table(table_name)

            indexes = evolver.get_indexes_for_table(table_name)

            for index_name, index_info in six.iteritems(indexes):
                self.add_index(table_name=table_name,
                               index_name=index_name,
                               columns=index_info['columns'],
                               unique=index_info['unique'])
    def test_set_db_index_true_and_existing_index(self):
        """Testing ChangeField with setting db_index=True and existing index
        in the database
        """
        class DestModel(models.Model):
            my_id = models.AutoField(primary_key=True)
            alt_pk = models.IntegerField()
            int_field = models.IntegerField(db_column='custom_db_column')
            int_field1 = models.IntegerField(db_index=True)
            int_field2 = models.IntegerField(db_index=True)
            int_field3 = models.IntegerField(unique=True)
            int_field4 = models.IntegerField(unique=False)
            char_field = models.CharField(max_length=20)
            char_field1 = models.CharField(max_length=25, null=True)
            char_field2 = models.CharField(max_length=30, null=False)
            m2m_field1 = models.ManyToManyField(
                ChangeAnchor1, db_table='change_field_non-default_m2m_table')

        evolver = EvolutionOperationsMulti('default',
                                           self.database_sig).get_evolver()
        index_name = evolver.get_default_index_name(
            'tests_testmodel', DestModel._meta.get_field('int_field2'))

        self.database_sig['tests_testmodel']['indexes'] = {
            index_name: {
                'unique': False,
                'columns': ['int_field2'],
            }
        }

        self.assertTrue(has_index_with_columns(
            self.database_sig, 'tests_testmodel', ['int_field2']))

        self.perform_evolution_tests(
            DestModel,
            [
                ChangeField('TestModel', 'int_field2', initial=None,
                            db_index=True),
            ],
            ("In model tests.TestModel:\n"
             "    In field 'int_field2':\n"
             "        Property 'db_index' has changed"),
            [
                "ChangeField('TestModel', 'int_field2', initial=None,"
                " db_index=True)",
            ],
            'AddDBIndexNoOpChangeModel',
            rescan_indexes=False)

        self.assertTrue(has_index_with_columns(
            self.test_database_sig, 'tests_testmodel', ['int_field2']))
示例#7
0
def rescan_indexes_for_database_sig(database_sig, database):
    evolver = EvolutionOperationsMulti(database).get_evolver()
    connection = evolver.connection
    introspection = connection.introspection
    cursor = connection.cursor()

    for table_name in introspection.get_table_list(cursor):
        table_sig = create_empty_database_table_sig()
        indexes = evolver.get_indexes_for_table(table_name)

        for index_name, index_info in indexes.iteritems():
            table_sig['indexes'][index_name] = index_info

        database_sig[table_name] = table_sig
    def test_set_db_index_true_and_existing_index(self):
        """Testing ChangeField with setting db_index=True and existing index
        in the database
        """
        class DestModel(models.Model):
            my_id = models.AutoField(primary_key=True)
            alt_pk = models.IntegerField()
            int_field = models.IntegerField(db_column='custom_db_column')
            int_field1 = models.IntegerField(db_index=True)
            int_field2 = models.IntegerField(db_index=True)
            int_field3 = models.IntegerField(unique=True)
            int_field4 = models.IntegerField(unique=False)
            char_field = models.CharField(max_length=20)
            char_field1 = models.CharField(max_length=25, null=True)
            char_field2 = models.CharField(max_length=30, null=False)
            m2m_field1 = models.ManyToManyField(
                ChangeAnchor1, db_table='change_field_non-default_m2m_table')

        evolver = EvolutionOperationsMulti('default',
                                           self.database_sig).get_evolver()
        index_name = evolver.get_default_index_name(
            'tests_testmodel', DestModel._meta.get_field('int_field2'))

        self.database_sig['tests_testmodel']['indexes'] = {
            index_name: {
                'unique': False,
                'columns': ['int_field2'],
            }
        }

        self.assertTrue(
            has_index_with_columns(self.database_sig, 'tests_testmodel',
                                   ['int_field2']))

        self.perform_evolution_tests(DestModel, [
            ChangeField('TestModel', 'int_field2', initial=None,
                        db_index=True),
        ], ("In model tests.TestModel:\n"
            "    In field 'int_field2':\n"
            "        Property 'db_index' has changed"), [
                "ChangeField('TestModel', 'int_field2', initial=None,"
                " db_index=True)",
            ],
                                     'AddDBIndexNoOpChangeModel',
                                     rescan_indexes=False)

        self.assertTrue(
            has_index_with_columns(self.test_database_sig, 'tests_testmodel',
                                   ['int_field2']))
示例#9
0
    def __init__(self, app_mutator, model_name, app_label, proj_sig,
                 database_sig, database):
        self.app_mutator = app_mutator
        self.model_name = model_name
        self.app_label = app_label
        self.database = (database or
                         get_database_for_model_name(app_label, model_name))
        self.can_simulate = True
        self._ops = []
        self._finalized = False

        assert self.database
        evolution_ops = EvolutionOperationsMulti(self.database,
                                                 self.database_sig)
        self.evolver = evolution_ops.get_evolver()
示例#10
0
    def __init__(self, app_mutator, model_name, app_label, proj_sig,
                 database_sig, database):
        self.app_mutator = app_mutator
        self.model_name = model_name
        self.app_label = app_label
        self.database = (database
                         or get_database_for_model_name(app_label, model_name))
        self.can_simulate = True
        self._ops = []
        self._finalized = False

        assert self.database
        evolution_ops = EvolutionOperationsMulti(self.database,
                                                 self.database_sig)
        self.evolver = evolution_ops.get_evolver()
示例#11
0
def write_sql(sql, database):
    "Output a list of SQL statements, unrolling parameters as required"
    evolver = EvolutionOperationsMulti(database).get_evolver()
    qp = evolver.quote_sql_param
    out_sql = []

    for statement in sql:
        if isinstance(statement, tuple):
            statement = unicode(
                statement[0] %
                tuple(qp(evolver.normalize_value(s)) for s in statement[1]))

        print statement
        out_sql.append(statement)

    return out_sql
示例#12
0
文件: mutations.py 项目: shash/IconDB
    def evolver(self, model, database=None):
        if is_multi_db() and database is None:
            db_name = router.db_for_write(model)
        else:
            db_name = database or 'default'

        return EvolutionOperationsMulti(db_name).get_evolver()
class DatabaseSigTests(TestCase):
    """Testing database signatures."""
    def setUp(self):
        self.database_sig = create_database_sig('default')
        self.evolver = EvolutionOperationsMulti('default').get_evolver()

    def test_initial_state(self):
        """Testing initial state of database_sig"""
        tables = self.database_sig.keys()

        # Check that a few known tables are in the list, to make sure
        # the scan worked.
        self.assertTrue('auth_permission' in tables)
        self.assertTrue('auth_user' in tables)
        self.assertTrue('django_evolution' in tables)
        self.assertTrue('django_project_version' in tables)

        self.assertTrue('indexes' in self.database_sig['django_evolution'])

        # Check the Evolution model
        index_name = self.evolver.get_default_index_name(
            Evolution._meta.db_table, Evolution._meta.get_field('version'))
        indexes = self.database_sig['django_evolution']['indexes']

        self.assertTrue(index_name in indexes)
        self.assertEqual(
            indexes[index_name],
            {
                'unique': False,
                'columns': ['version_id'],
            })
示例#14
0
    def evolver(self, model):
        db_name = None

        if is_multi_db():
            db_name = router.db_for_write(model)

        return EvolutionOperationsMulti(db_name).get_evolver()
示例#15
0
    def __enter__(self):
        """Enter the context manager.

        This will prepare internal state for execution, and optionally disable
        constraint checking (if requested during construction).

        The context manager must be entered before operations will work.

        Context:
            SQLExecutor:
            This instance.
        """
        connection = self._connection
        database = self._database

        if (connection.in_atomic_block
                and not connection.features.can_rollback_ddl):
            logger.warning('Some database schema modifications may not be '
                           'able to be rolled back on this database if '
                           'something goes wrong.')

        if not self._check_constraints:
            self._constraints_disabled = \
                connection.disable_constraint_checking()

        self._cursor = connection.cursor()
        self._evolver_backend = \
            EvolutionOperationsMulti(database).get_evolver()

        return self
示例#16
0
def write_sql(sql, database):
    "Output a list of SQL statements, unrolling parameters as required"
    evolver = EvolutionOperationsMulti(database).get_evolver()
    qp = evolver.quote_sql_param
    out_sql = []

    for statement in sql:
        if isinstance(statement, tuple):
            statement = unicode(statement[0] % tuple(
                qp(evolver.normalize_value(s))
                for s in statement[1]
            ))

        print statement
        out_sql.append(statement)

    return out_sql
示例#17
0
def write_sql(sql, database):
    "Output a list of SQL statements, unrolling parameters as required"
    qp = EvolutionOperationsMulti(database).get_evolver().quote_sql_param

    for statement in sql:
        if isinstance(statement, tuple):
            print unicode(statement[0] % tuple(qp(s) for s in statement[1]))
        else:
            print unicode(statement)
示例#18
0
    def get_evolver(self):
        """Return an evolver for the database.

        Returns:
            django_evolution.db.EvolutionOperationsMulti:
            The database evolver for this type of database.
        """
        return EvolutionOperationsMulti(self.database,
                                        self.database_state).get_evolver()
示例#19
0
def rescan_indexes_for_database_sig(database_sig, database):
    evolver = EvolutionOperationsMulti(database).get_evolver()
    connection = evolver.connection
    introspection = connection.introspection
    cursor = connection.cursor()

    for table_name in introspection.get_table_list(cursor):
        if hasattr(table_name, 'name'):
            # Django >= 1.7
            table_name = table_name.name

        table_sig = create_empty_database_table_sig()
        indexes = evolver.get_indexes_for_table(table_name)

        for index_name, index_info in indexes.iteritems():
            table_sig['indexes'][index_name] = index_info

        database_sig[table_name] = table_sig
示例#20
0
def execute_sql(cursor, sql, database):
    """
    Execute a list of SQL statements on the provided cursor, unrolling
    parameters as required
    """
    evolver = EvolutionOperationsMulti(database).get_evolver()

    for statement in sql:
        if isinstance(statement, tuple):
            statement = (statement[0].strip(), statement[1])

            if statement[0] and not statement[0].startswith('--'):
                cursor.execute(statement[0], tuple(
                    evolver.normalize_value(s)
                    for s in statement[1]
                ))
        else:
            statement = statement.strip()

            if statement and not statement.startswith('--'):
                cursor.execute(statement)
示例#21
0
def execute_sql(cursor, sql, database):
    """Execute a list of SQL statements.

    Args:
        cursor (object):
            The database backend's cursor.

        sql (list):
            A list of SQL statements. Each entry might be a string, or a
            tuple consisting of a format string and formatting arguments.

        database (unicode):
            The database the SQL statements would be executed on.
    """
    evolver = EvolutionOperationsMulti(database).get_evolver()
    statement = None

    try:
        for statement in sql:
            if isinstance(statement, tuple):
                statement = (statement[0].strip(), statement[1])

                if statement[0] and not statement[0].startswith('--'):
                    cursor.execute(statement[0], tuple(
                        evolver.normalize_value(s)
                        for s in statement[1]
                    ))
            else:
                statement = statement.strip()

                if statement and not statement.startswith('--'):
                    cursor.execute(statement)
    except Exception as e:
        # Augment the exception so that callers can get the SQL statement
        # that failed.
        e.last_sql_statement = statement

        raise
示例#22
0
def get_default_tablespace(db_name):
    """Return the default tablespace for a database.

    Version Added:
        2.2

    Args:
        db_name (unicode):
            The name of the database.

    Returns:
        unicode:
        The default tablespace for the database, or ``None``.
    """
    evolver = EvolutionOperationsMulti(db_name).get_evolver()

    return evolver.default_tablespace
示例#23
0
    def evolver(self, model, database_state, database=None):
        if database is None:
            database = get_database_for_model_name(model.app_label,
                                                   model.model_name)

        return EvolutionOperationsMulti(database, database_state).get_evolver()
示例#24
0
def register_models(database_state, models, register_indexes=False,
                    new_app_label='tests', db_name='default', app=evo_test):
    """Register models for testing purposes.

    Args:
        database_state (django_evolution.db.state.DatabaseState):
            The database state to populate with model information.

        models (list of django.db.models.Model):
            The models to register.

        register_indexes (bool, optional):
            Whether indexes should be registered for any models. Defaults to
            ``False``.

        new_app_label (str, optional):
            The label for the test app. Defaults to "tests".

        db_name (str, optional):
            The name of the database connection. Defaults to "default".

        app (module, optional):
            The application module for the test models.

    Returns:
        collections.OrderedDict:
        A dictionary of registered models. The keys are model names, and
        the values are the models.
    """
    app_cache = OrderedDict()
    evolver = EvolutionOperationsMulti(db_name, database_state).get_evolver()

    db_connection = connections[db_name or DEFAULT_DB_ALIAS]
    max_name_length = db_connection.ops.max_name_length()

    for new_object_name, model in reversed(models):
        # Grab some state from the model's meta instance. Some of this will
        # be original state that we'll keep around to help us unregister old
        # values and compute new ones.
        meta = model._meta

        orig_app_label = meta.app_label
        orig_db_table = meta.db_table
        orig_object_name = meta.object_name
        orig_model_name = get_model_name(model)

        # Find out if the table name being used is a custom table name, or
        # one generated by Django.
        new_model_name = new_object_name.lower()
        new_db_table = orig_db_table

        generated_db_table = truncate_name(
            '%s_%s' % (orig_app_label, orig_model_name),
            max_name_length)

        if orig_db_table == generated_db_table:
            # It was a generated one, so replace it with a version containing
            # the new model and app names.
            new_db_table = truncate_name('%s_%s' % (new_app_label,
                                                    new_model_name),
                                         max_name_length)
            meta.db_table = new_db_table

        # Set the new app/model names back on the meta instance.
        meta.app_label = new_app_label
        meta.object_name = new_object_name
        set_model_name(model, new_model_name)

        # Add an entry for the table in the database state, if it's not
        # already there.
        if not database_state.has_table(new_db_table):
            database_state.add_table(new_db_table)

        if register_indexes:
            # Now that we definitely have an entry, store the indexes for
            # all the fields in the database state, so that other operations
            # can look up the index names.
            for field in meta.local_fields:
                if field.db_index or field.unique:
                    new_index_name = create_index_name(
                        db_connection,
                        new_db_table,
                        field_names=[field.name],
                        col_names=[field.column],
                        unique=field.unique)

                    database_state.add_index(
                        index_name=new_index_name,
                        table_name=new_db_table,
                        columns=[field.column],
                        unique=field.unique)

            for field_names in meta.unique_together:
                fields = evolver.get_fields_for_names(model, field_names)
                new_index_name = create_index_name(
                    db_connection,
                    new_db_table,
                    field_names=field_names,
                    unique=True)

                database_state.add_index(
                    index_name=new_index_name,
                    table_name=new_db_table,
                    columns=[field.column for field in fields],
                    unique=True)

            for field_names in getattr(meta, 'index_together', []):
                # Django >= 1.5
                fields = evolver.get_fields_for_names(model, field_names)
                new_index_name = create_index_together_name(
                    db_connection,
                    new_db_table,
                    field_names=[field.name for field in fields])

                database_state.add_index(
                    index_name=new_index_name,
                    table_name=new_db_table,
                    columns=[field.column for field in fields])

            if getattr(meta, 'indexes', None):
                # Django >= 1.11
                for index, orig_index in zip(meta.indexes,
                                             meta.original_attrs['indexes']):
                    if not orig_index.name:
                        # The name was auto-generated. We'll need to generate
                        # it again for the new table name.
                        index.set_name_with_model(model)

                    fields = evolver.get_fields_for_names(
                        model, index.fields, allow_sort_prefixes=True)
                    database_state.add_index(
                        index_name=index.name,
                        table_name=new_db_table,
                        columns=[field.column for field in fields])

        # ManyToManyFields have their own tables, which will also need to be
        # renamed. Go through each of them and figure out what changes need
        # to be made.
        for field in meta.local_many_to_many:
            through = get_remote_field(field).through

            if not through:
                continue

            through_meta = through._meta
            through_orig_model_name = get_model_name(through)
            through_new_model_name = through_orig_model_name

            # Find out if the through table name is a custom table name, or
            # one generated by Django.
            generated_db_table = truncate_name(
                '%s_%s' % (orig_db_table, field.name),
                max_name_length)

            if through_meta.db_table == generated_db_table:
                # This is an auto-generated table name. Start changing the
                # state for it.
                assert through_meta.app_label == orig_app_label
                through_meta.app_label = new_app_label

                # Transform the 'through' table information only if we've
                # transformed the parent db_table.
                if new_db_table != orig_db_table:
                    through_meta.db_table = truncate_name(
                        '%s_%s' % (new_db_table, field.name),
                        max_name_length)

                    through_meta.object_name = \
                        through_meta.object_name.replace(orig_object_name,
                                                         new_object_name)

                    through_new_model_name = \
                        through_orig_model_name.replace(orig_model_name,
                                                        new_model_name)
                    set_model_name(through, through_new_model_name)

            # Change each of the columns for the fields on the
            # ManyToManyField's model to reflect the new model names.
            for through_field in through._meta.local_fields:
                through_remote_field = get_remote_field(through_field)

                if (through_remote_field and
                    get_remote_field_model(through_remote_field)):
                    column = through_field.column

                    if (column.startswith((orig_model_name,
                                           'to_%s' % orig_model_name,
                                           'from_%s' % orig_model_name))):
                        # This is a field that references one end of the
                        # relation or another. Update the model naem in the
                        # field's column.
                        through_field.column = column.replace(orig_model_name,
                                                              new_model_name)

            # Replace the entry in the models cache for the through table,
            # removing the old name and adding the new one.
            if through_orig_model_name in all_models[orig_app_label]:
                unregister_app_model(orig_app_label, through_orig_model_name)

            app_cache[through_new_model_name] = through
            register_app_models(new_app_label,
                                [(through_new_model_name, through)])

        # Unregister with the old model name and register the new one.
        if orig_model_name in all_models[orig_app_label]:
            unregister_app_model(orig_app_label, orig_model_name)

        register_app_models(new_app_label, [(new_model_name, model)])
        app_cache[new_model_name] = model

    # If the app hasn't yet been registered, do that now.
    if not is_app_registered(app):
        register_app(new_app_label, app)

    return app_cache
示例#25
0
def _register_models(database_sig, app_label='tests', db_name='default',
                     app=evo_test, *models, **kwargs):
    """Register models for testing purposes.

    Args:
        database_sig (dict):
            The database signature to populate with model information.

        app_label (str, optional):
            The label for the test app. Defaults to "tests".

        db_name (str, optional):
            The name of the database connection. Defaults to "default".

        app (module, optional):
            The application module for the test models.

        *models (tuple):
            The models to register.

        **kwargs (dict):
            Additional keyword arguments. This supports:

            ``register_indexes``:
                Specifies whether indexes should be registered for any
                models. Defaults to ``False``.

    Returns:
        collections.OrderedDict:
        A dictionary of registered models. The keys are model names, and
        the values are the models.
    """
    django_evolution_models = all_models['django_evolution']

    app_cache = OrderedDict()
    evolver = EvolutionOperationsMulti(db_name, database_sig).get_evolver()
    register_indexes = kwargs.get('register_indexes', False)

    my_connection = connections[db_name or DEFAULT_DB_ALIAS]
    max_name_length = my_connection.ops.max_name_length()

    for name, model in reversed(models):
        orig_model_name = get_model_name(model)

        if orig_model_name in django_evolution_models:
            unregister_app_model('django_evolution', orig_model_name)

        orig_db_table = model._meta.db_table
        orig_object_name = model._meta.object_name

        generated_db_table = truncate_name(
            '%s_%s' % (model._meta.app_label, orig_model_name),
            max_name_length)

        if orig_db_table.startswith(generated_db_table):
            model._meta.db_table = '%s_%s' % (app_label, name.lower())

        model._meta.db_table = truncate_name(model._meta.db_table,
                                             max_name_length)
        model._meta.app_label = app_label
        model._meta.object_name = name
        model_name = name.lower()
        set_model_name(model, model_name)

        # Add an entry for the table in database_sig, if it's not already
        # there.
        if model._meta.db_table not in database_sig:
            database_sig[model._meta.db_table] = \
                signature.create_empty_database_table_sig()

        if register_indexes:
            # Now that we definitely have an entry, store the indexes for
            # all the fields in database_sig, so that other operations can
            # look up the index names.
            for field in model._meta.local_fields:
                if field.db_index or field.unique:
                    index_name = create_index_name(
                        my_connection,
                        model._meta.db_table,
                        field_names=[field.name],
                        col_names=[field.column],
                        unique=field.unique)

                    signature.add_index_to_database_sig(
                        evolver, database_sig, model, [field],
                        index_name=index_name,
                        unique=field.unique)

            for field_names in model._meta.unique_together:
                index_name = create_index_name(
                    my_connection,
                    model._meta.db_table,
                    field_names=field_names,
                    unique=True)

                signature.add_index_to_database_sig(
                    evolver, database_sig, model,
                    evolver.get_fields_for_names(model, field_names),
                    index_name=index_name,
                    unique=True)

            for field_names in getattr(model._meta, 'index_together', []):
                fields = evolver.get_fields_for_names(model, field_names)
                index_name = create_index_name(
                    my_connection,
                    model._meta.db_table,
                    field_names=[field.name for field in fields],
                    col_names=[field.column for field in fields])

                signature.add_index_to_database_sig(
                    evolver, database_sig, model,
                    fields,
                    index_name=index_name)

        # Register the model with the app.
        add_app_test_model(model, app_label=app_label)

        for field in model._meta.local_many_to_many:
            if not field.rel.through:
                continue

            through = field.rel.through

            generated_db_table = truncate_name(
                '%s_%s' % (orig_db_table, field.name),
                max_name_length)

            if through._meta.db_table == generated_db_table:
                through._meta.app_label = app_label

                # Transform the 'through' table information only
                # if we've transformed the parent db_table.
                if model._meta.db_table != orig_db_table:
                    through._meta.db_table = \
                        '%s_%s' % (model._meta.db_table, field.name)

                    through._meta.object_name = \
                        through._meta.object_name.replace(
                            orig_object_name,
                            model._meta.object_name)

                    set_model_name(
                        through,
                        get_model_name(through).replace(orig_model_name,
                                                        model_name))

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

            for field in through._meta.local_fields:
                if field.rel and field.rel.to:
                    column = field.column

                    if (column.startswith(orig_model_name) or
                        column.startswith('to_%s' % orig_model_name) or
                        column.startswith('from_%s' % orig_model_name)):

                        field.column = column.replace(
                            orig_model_name,
                            get_model_name(model))

            through_model_name = get_model_name(through)

            if through_model_name in django_evolution_models:
                unregister_app_model('django_evolution', through_model_name)

            app_cache[through_model_name] = through
            add_app_test_model(through, app_label=app_label)

        app_cache[model_name] = model

    if not is_app_registered(app):
        register_app(app_label, app)

    return app_cache
示例#26
0
 def setUp(self):
     self.database_sig = create_database_sig('default')
     self.evolver = EvolutionOperationsMulti('default').get_evolver()
 def setUp(self):
     self.database_sig = create_database_sig('default')
     self.evolver = EvolutionOperationsMulti('default').get_evolver()
示例#28
0
def register_models(database_state, models, register_indexes=False,
                    new_app_label='tests', db_name='default', app=evo_test):
    """Register models for testing purposes.

    Args:
        database_state (django_evolution.db.state.DatabaseState):
            The database state to populate with model information.

        models (list of django.db.models.Model):
            The models to register.

        register_indexes (bool, optional):
            Whether indexes should be registered for any models. Defaults to
            ``False``.

        new_app_label (str, optional):
            The label for the test app. Defaults to "tests".

        db_name (str, optional):
            The name of the database connection. Defaults to "default".

        app (module, optional):
            The application module for the test models.

    Returns:
        collections.OrderedDict:
        A dictionary of registered models. The keys are model names, and
        the values are the models.
    """
    app_cache = OrderedDict()
    evolver = EvolutionOperationsMulti(db_name, database_state).get_evolver()

    db_connection = connections[db_name or DEFAULT_DB_ALIAS]
    max_name_length = db_connection.ops.max_name_length()

    for new_object_name, model in reversed(models):
        # Grab some state from the model's meta instance. Some of this will
        # be original state that we'll keep around to help us unregister old
        # values and compute new ones.
        meta = model._meta

        orig_app_label = meta.app_label
        orig_db_table = meta.db_table
        orig_object_name = meta.object_name
        orig_model_name = get_model_name(model)

        # Find out if the table name being used is a custom table name, or
        # one generated by Django.
        new_model_name = new_object_name.lower()
        new_db_table = orig_db_table

        generated_db_table = truncate_name(
            '%s_%s' % (orig_app_label, orig_model_name),
            max_name_length)

        if orig_db_table == generated_db_table:
            # It was a generated one, so replace it with a version containing
            # the new model and app names.
            new_db_table = truncate_name('%s_%s' % (new_app_label,
                                                    new_model_name),
                                         max_name_length)
            meta.db_table = new_db_table

        # Set the new app/model names back on the meta instance.
        meta.app_label = new_app_label
        meta.object_name = new_object_name
        set_model_name(model, new_model_name)

        # Add an entry for the table in the database state, if it's not
        # already there.
        if not database_state.has_table(new_db_table):
            database_state.add_table(new_db_table)

        if register_indexes:
            # Now that we definitely have an entry, store the indexes for
            # all the fields in the database state, so that other operations
            # can look up the index names.
            for field in meta.local_fields:
                if field.db_index or field.unique:
                    new_index_name = create_index_name(
                        db_connection,
                        new_db_table,
                        field_names=[field.name],
                        col_names=[field.column],
                        unique=field.unique)

                    database_state.add_index(
                        index_name=new_index_name,
                        table_name=new_db_table,
                        columns=[field.column],
                        unique=field.unique)

            for field_names in meta.unique_together:
                fields = evolver.get_fields_for_names(model, field_names)
                new_index_name = create_index_name(
                    db_connection,
                    new_db_table,
                    field_names=field_names,
                    unique=True)

                database_state.add_index(
                    index_name=new_index_name,
                    table_name=new_db_table,
                    columns=[field.column for field in fields],
                    unique=True)

            for field_names in getattr(meta, 'index_together', []):
                # Django >= 1.5
                fields = evolver.get_fields_for_names(model, field_names)
                new_index_name = create_index_together_name(
                    db_connection,
                    new_db_table,
                    field_names=[field.name for field in fields])

                database_state.add_index(
                    index_name=new_index_name,
                    table_name=new_db_table,
                    columns=[field.column for field in fields])

            if getattr(meta, 'indexes', None):
                # Django >= 1.11
                for index, orig_index in zip(meta.indexes,
                                             meta.original_attrs['indexes']):
                    if not orig_index.name:
                        # The name was auto-generated. We'll need to generate
                        # it again for the new table name.
                        index.set_name_with_model(model)

                    fields = evolver.get_fields_for_names(
                        model, index.fields, allow_sort_prefixes=True)
                    database_state.add_index(
                        index_name=index.name,
                        table_name=new_db_table,
                        columns=[field.column for field in fields])

        # ManyToManyFields have their own tables, which will also need to be
        # renamed. Go through each of them and figure out what changes need
        # to be made.
        for field in meta.local_many_to_many:
            through = get_remote_field(field).through

            if not through:
                continue

            through_meta = through._meta
            through_orig_model_name = get_model_name(through)
            through_new_model_name = through_orig_model_name

            # Find out if the through table name is a custom table name, or
            # one generated by Django.
            generated_db_table = truncate_name(
                '%s_%s' % (orig_db_table, field.name),
                max_name_length)

            if through_meta.db_table == generated_db_table:
                # This is an auto-generated table name. Start changing the
                # state for it.
                assert through_meta.app_label == orig_app_label
                through_meta.app_label = new_app_label

                # Transform the 'through' table information only if we've
                # transformed the parent db_table.
                if new_db_table != orig_db_table:
                    through_meta.db_table = truncate_name(
                        '%s_%s' % (new_db_table, field.name),
                        max_name_length)

                    through_meta.object_name = \
                        through_meta.object_name.replace(orig_object_name,
                                                         new_object_name)

                    through_new_model_name = \
                        through_orig_model_name.replace(orig_model_name,
                                                        new_model_name)
                    set_model_name(through, through_new_model_name)

            # Change each of the columns for the fields on the
            # ManyToManyField's model to reflect the new model names.
            for through_field in through._meta.local_fields:
                through_remote_field = get_remote_field(through_field)

                if (through_remote_field and
                    get_remote_field_model(through_remote_field)):
                    column = through_field.column

                    if (column.startswith((orig_model_name,
                                           'to_%s' % orig_model_name,
                                           'from_%s' % orig_model_name))):
                        # This is a field that references one end of the
                        # relation or another. Update the model naem in the
                        # field's column.
                        through_field.column = column.replace(orig_model_name,
                                                              new_model_name)

            # Replace the entry in the models cache for the through table,
            # removing the old name and adding the new one.
            if through_orig_model_name in all_models[orig_app_label]:
                unregister_app_model(orig_app_label, through_orig_model_name)

            app_cache[through_new_model_name] = through
            register_app_models(new_app_label,
                                [(through_new_model_name, through)])

        # Unregister with the old model name and register the new one.
        if orig_model_name in all_models[orig_app_label]:
            unregister_app_model(orig_app_label, orig_model_name)

        register_app_models(new_app_label, [(new_model_name, model)])
        app_cache[new_model_name] = model

    # If the app hasn't yet been registered, do that now.
    if not is_app_registered(app):
        register_app(new_app_label, app)

    return app_cache