Exemple #1
0
 def test_dependence_sort07(self):
     DS = self.DepSortable
     A = DS('End', ['Middle'])
     B = DS('Start')
     C = DS('Middle', ['Start'])
     self.assertEqual([B, C, A], dependence_sort([A, B, C], DS.key,
                                                 DS.deps))
Exemple #2
0
 def test_dependence_sort03(self):
     DS = self.DepSortable
     A = DS('A', ['B'])
     B = DS('B')
     self.assertEqual([B, A],
                      dependence_sort([A, B], DS.key, DS.deps)
                     )
Exemple #3
0
 def test_dependence_sort02(self):
     A = self.DepSortable('A')
     B = self.DepSortable('B')
     self.assertListEqual(
         [A, B],
         dependence_sort([A, B], lambda ds: ds.name, lambda ds: ds.dependencies),
     )
Exemple #4
0
 def test_dependence_sort05(self):
     DS = self.DepSortable
     A = DS('A', ['C', 'D'])
     B = DS('B')
     C = DS('C', ['B'])
     D = DS('D')
     self.assertIn(dependence_sort([A, B, C, D], DS.key, DS.deps), (
         [B, D, C, A],
         [B, C, D, A],
         [D, B, C, A],
     ))
Exemple #5
0
 def test_dependence_sort01(self):
     self.assertEqual([], dependence_sort([], lambda ds: ds.name, lambda ds: ds.dependencies))
def ordered_models_to_delete(app_config, connection):
    """Models of the given app to delete.
    @return A tuple (models, loop_error).
            'models' is a list of the models classes to delete ;
                     the order respects the dependencies between the models.
            'loop_error' is a boolean which indicates dependencies loop error.
    """
    from django.db import router
    from django.utils.datastructures import OrderedSet

    from creme.creme_core.utils.dependence_sort import dependence_sort, DependenciesLoopError

    class ModelInfo:
        # def __init__(self, model, dependencies, sql_cmd):
        def __init__(self, model, dependencies):
            self.model = model
            self.dependencies = dependencies
            # self.sql_cmd = sql_cmd

        def __str__(self):
            return 'ModelInfo(model={model}, dependencies={dependencies})'.format(
                model=self.model.__name__,
                dependencies=[d.__name__ for d in self.dependencies],
            )

    models_info = []
    cursor = connection.cursor()

    try:
        table_names = set(connection.introspection.table_names(cursor))
        app_models = OrderedSet(
            router.get_migratable_models(
                app_config,
                connection.alias,
                # include_auto_created=True,
                # NB: the auto created tables are automatically
                #     deleted by schema_editor.delete_model(model)
                include_auto_created=False,
            ))

        for model in app_models:
            meta = model._meta

            if connection.introspection.table_name_converter(
                    meta.db_table) in table_names:
                # dependencies = []
                dependencies = set()  # We use a set to avoid duplicates

                for f in meta.local_fields:
                    # if f.rel:
                    if f.remote_field:
                        # related_model = f.rel.to
                        related_model = f.remote_field.model

                        # if related_model in app_models:
                        # NB: we avoid self-referencing (TODO: improve dependence_sort() ?)
                        if related_model is not model and related_model in app_models:
                            # dependencies.append(related_model)
                            dependencies.add(related_model)

                models_info.append(
                    ModelInfo(
                        model=model,
                        dependencies=dependencies,
                        # sql_cmd=connection.creation.sql_destroy_model(model, [], style)[0],
                    ))
    finally:
        cursor.close()

    dep_error = False
    try:
        models_info = dependence_sort(
            models_info,
            get_key=lambda mi: mi.model,
            get_dependencies=lambda mi: mi.dependencies,
        )
    except DependenciesLoopError:
        dep_error = True
    else:
        models_info.reverse()  # The dependencies must be deleted _after_

    # return [mi.sql_cmd for mi in models_info], dep_error
    return [mi.model for mi in models_info], dep_error
    def handle(self, *app_labels, **options):
        verbosity = options.get('verbosity')

        # eg: 'persons', 'creme_core'...
        all_apps = OrderedSet(app_config.label
                              for app_config in creme_app_configs())

        apps_2_populate = all_apps if not app_labels else \
                          [_checked_app_label(app, all_apps) for app in app_labels]

        # ----------------------------------------------------------------------
        populators = []
        populators_names = set()  # Names of populators that will be run
        total_deps = set(
        )  # Populators names that are needed by our populators
        total_missing_deps = set()  # All populators names that are added by
        # this script because of dependencies

        while True:
            changed = False

            for app_label in apps_2_populate:
                populator = self._get_populator(app_label=app_label,
                                                verbosity=verbosity,
                                                all_apps=all_apps,
                                                options=options)

                if populator is not None:
                    populators.append(populator)
                    populators_names.add(app_label)
                    total_deps.update(populator.dependencies)
                    changed = True

            if not changed: break

            apps_2_populate = total_deps - populators_names
            total_missing_deps |= apps_2_populate

        if total_missing_deps and verbosity >= 1:
            self.stdout.write(
                'Additional dependencies will be populated: {}'.format(
                    ', '.join(total_missing_deps)), self.style.NOTICE)

        # Clean the dependencies (avoid dependencies that do not exist in
        # 'populators', which would cause Exception raising)
        for populator in populators:
            populator.build_dependencies(populators_names)

        populators = dependence_sort(
            populators,
            BasePopulator.get_app,
            BasePopulator.get_dependencies,
        )

        # ----------------------------------------------------------------------
        self.models = set()
        dispatch_uid = 'creme_core-populate_command'

        pre_save.connect(self._signal_handler, dispatch_uid=dispatch_uid)

        for populator in populators:
            if verbosity >= 1:
                self.stdout.write('Populate "{}" ...'.format(populator.app),
                                  ending='')
                self.stdout.flush()

            try:
                populator.populate()
            except Exception as e:
                self.stderr.write(' Populate "{}" failed ({})'.format(
                    populator.app, e))
                if verbosity >= 1:
                    exc_type, exc_value, exc_traceback = sys.exc_info()
                    self.stderr.write(''.join(
                        format_exception(exc_type, exc_value, exc_traceback)))

            if verbosity >= 1:
                self.stdout.write(' OK', self.style.SUCCESS)

        pre_save.disconnect(dispatch_uid=dispatch_uid)

        # ----------------------------------------------------------------------
        if self.models:
            if verbosity >= 1:
                self.stdout.write(
                    'Update sequences for models : {}'.format(
                        [model.__name__ for model in self.models]),
                    ending='',
                )
                self.stdout.flush()

            connection = connections[options.get('database', DEFAULT_DB_ALIAS)]
            cursor = connection.cursor()

            for line in connection.ops.sequence_reset_sql(
                    no_style(), self.models):
                cursor.execute(line)

            # connection.close() #seems useless (& does not work with mysql)

            if verbosity >= 1:
                self.stdout.write(self.style.SUCCESS(' OK'))
        elif verbosity >= 1:
            self.stdout.write('No sequence to update.')

        if verbosity >= 1:
            self.stdout.write(self.style.SUCCESS('Populate is OK.'))