Example #1
0
def test_migrate_command_with_django_table(django_db_setup_no_init, settings):
    """
    This test simulate the case when a project
    was created to be used with django migrations table
    Either because it didn't use septentrion-based django-north,
    or a pre septentrion-base django-north version.
    """
    connection = connections['no_init']

    # We begin with an empty database
    assert migrations.get_current_version(connection) is None

    # We simulate the setup of the database in the past,
    # with a django_migrations table.
    updated_settings = septentrion_settings(connection)
    updated_settings.update({
        "target_version": "1.0",
        "table": "django_migrations",
    })
    septentrion.migrate(**updated_settings)

    # DB is initialized, this doesn't return None
    assert migrations.get_current_version(connections['no_init']) is not None

    # and migrate to newer version
    call_command('migrate', '--database', 'no_init')

    # check if max applied version is target version
    assert settings.NORTH_TARGET_VERSION != "1.0"
    assert (migrations.get_applied_versions(
        connections['no_init'])[-1] == settings.NORTH_TARGET_VERSION)
Example #2
0
def test_get_current_version(settings, mocker):
    mock_table = mocker.patch(
        'django_north.management.migrations'
        '.get_current_version_from_table',
        return_value='from_table')
    mock_comment = mocker.patch(
        'django_north.management.migrations'
        '.get_current_version_from_comment',
        return_value='from_comment')

    # no setting
    if hasattr(settings, 'NORTH_CURRENT_VERSION_DETECTOR'):
        del settings.NORTH_CURRENT_VERSION_DETECTOR

    result = migrations.get_current_version()
    assert mock_table.called is True
    assert mock_comment.called is False
    assert result == 'from_table'

    # wrong setting
    mock_table.reset_mock()
    mock_comment.reset_mock()
    settings.NORTH_CURRENT_VERSION_DETECTOR = (
        'django_north.management.migrations'
        '.get_current_version_from_foo')

    with pytest.raises(AttributeError):
        migrations.get_current_version()
    assert mock_table.called is False
    assert mock_comment.called is False

    # good setting - table
    mock_table.reset_mock()
    mock_comment.reset_mock()
    settings.NORTH_CURRENT_VERSION_DETECTOR = (
        'django_north.management.migrations'
        '.get_current_version_from_table')

    result = migrations.get_current_version()
    assert mock_table.called is True
    assert mock_comment.called is False
    assert result == 'from_table'

    # good setting - comment
    mock_table.reset_mock()
    mock_comment.reset_mock()
    settings.NORTH_CURRENT_VERSION_DETECTOR = (
        'django_north.management.migrations'
        '.get_current_version_from_comment')

    result = migrations.get_current_version()
    assert mock_table.called is False
    assert mock_comment.called is True
    assert result == 'from_comment'
Example #3
0
def test_migrate_command_for_real(django_db_setup_no_init, settings):
    # from scratch, septentrion will create a migrations table itself

    # if DB is not initialized, this return None
    assert migrations.get_current_version(connections['no_init']) is None

    call_command('migrate', '--database', 'no_init')

    assert migrations.get_current_version(connections['no_init']) is not None

    # check if max applied version is target version
    assert (migrations.get_applied_versions(
        connections['no_init'])[-1] == settings.NORTH_TARGET_VERSION)
Example #4
0
    def flush(self, **options):
        database = options.get('database')
        connection = connections[database]
        verbosity = options.get('verbosity')
        interactive = options.get('interactive')
        # The following are stealth options used by Django's internals.
        reset_sequences = options.get('reset_sequences', True)
        allow_cascade = options.get('allow_cascade', False)
        inhibit_post_migrate = options.get('inhibit_post_migrate', False)

        self.style = no_style()

        # Import the 'management' module within each installed app, to register
        # dispatcher events.
        for app_config in apps.get_app_configs():
            try:
                import_module('.management', app_config.name)
            except ImportError:
                pass

        # custom: only_django False
        # get current version before flush
        current_version = get_current_version(connection)
        sql_list = sql_flush(self.style,
                             connection,
                             only_django=False,
                             reset_sequences=reset_sequences,
                             allow_cascade=allow_cascade)

        if interactive:
            confirm = input("""You have requested a flush of the database.
This will IRREVERSIBLY DESTROY all data currently in the %r database,
and return each table to an empty state.
Are you sure you want to do this?

    Type 'yes' to continue, or 'no' to cancel: """ %
                            connection.settings_dict['NAME'])
        else:
            confirm = 'yes'

        if confirm == 'yes':
            try:
                with transaction.atomic(
                        using=database,
                        savepoint=connection.features.can_rollback_ddl):
                    with connection.cursor() as cursor:
                        for sql in sql_list:
                            cursor.execute(sql)
            except Exception as e:
                new_msg = (
                    "Database %s couldn't be flushed. Possible reasons:\n"
                    "  * The database isn't running or isn't configured "
                    "correctly.\n"
                    "  * At least one of the expected database tables doesn't "
                    "exist.\n"
                    "  * The SQL was invalid.\n"
                    "Hint: Look at the output of 'django-admin sqlflush'. "
                    "That's the SQL this command wasn't able to run.\n"
                    "The full error: %s") % (connection.settings_dict['NAME'],
                                             e)
                raise CommandError(new_msg) from e

            if not inhibit_post_migrate:
                self.emit_post_migrate(verbosity, interactive, database,
                                       current_version)

            # Reinstall the initial_data fixture.
            if options.get('load_initial_data'):
                # Remove any option that is not handle by loaddata
                # We need to load loaddata command, get its parser to extract
                # valid options
                app_name = get_commands()['loaddata']
                command = load_command_class(app_name, 'loaddata')
                parser = command.create_parser('loaddata', 'initial_data')
                valid_options = [
                    action.dest for action in parser._actions
                    if action.option_strings
                ]
                app_options = {
                    k: v
                    for k, v in options.items() if k in valid_options
                }
                # Reinstall the initial_data fixture for apps without
                # migrations.
                from django.db.migrations.executor import MigrationExecutor
                executor = MigrationExecutor(connection)
                for app_label in executor.loader.unmigrated_apps:
                    app_options['app_label'] = app_label
                    try:
                        call_command('loaddata', 'initial_data', **app_options)
                    except CommandError:
                        # fails with django 1.10 if initial_data does not exist
                        pass
        else:
            self.stdout.write("Flush cancelled.\n")