diff --git a/trytond/ir/model.py b/trytond/ir/model.py index 2778bd3be4..a69f3b3ec9 100644 --- a/trytond/ir/model.py +++ b/trytond/ir/model.py @@ -1,8 +1,9 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. -import re import heapq import json +import logging +import re from sql import Null from sql.aggregate import Max @@ -33,6 +34,7 @@ __all__ = [ 'ModelData', 'PrintModelGraphStart', 'PrintModelGraph', 'ModelGraph', 'ModelWorkflowGraph', ] +logger = logging.getLogger(__name__) class Model(ModelSQL, ModelView): @@ -105,6 +107,26 @@ class Model(ModelSQL, ModelView): where=ir_model.id == model_id)) return model_id + @classmethod + def clean(cls): + pool = Pool() + transaction = Transaction() + cursor = transaction.connection.cursor() + ir_model = cls.__table__() + cursor.execute(*ir_model.select(ir_model.model, ir_model.id)) + for model, id_ in cursor: + try: + pool.get(model) + except KeyError: + logger.info("remove model: %s", model) + try: + cls.delete([cls(id_)]) + transaction.commit() + except Exception: + transaction.rollback() + logger.error( + "could not delete model: %s", model, exc_info=True) + @classmethod def list_models(cls): 'Return a list of all models names' @@ -259,6 +281,29 @@ class ModelField(ModelSQL, ModelView): where=ir_model_field.id == model_fields[field_name]['id'])) + @classmethod + def clean(cls): + pool = Pool() + IrModel = pool.get('ir.model') + transaction = Transaction() + cursor = transaction.connection.cursor() + ir_model = IrModel.__table__() + ir_model_field = cls.__table__() + cursor.execute(*ir_model_field + .join(ir_model, condition=ir_model_field.model == ir_model.id) + .select(ir_model.model, ir_model_field.name, ir_model_field.id)) + for model, field, id_ in cursor: + Model = pool.get(model) + if field not in Model._fields: + logger.info("remove field: %s.%s", model, field) + try: + cls.delete([cls(id_)]) + transaction.commit() + except Exception: + transaction.rollback() + logger.error( + "could not delete field: %s.%s", model, field, + exc_info=True) @staticmethod def default_name(): diff --git a/trytond/modules/__init__.py b/trytond/modules/__init__.py index 2ce376d889..d968d44c5c 100644 --- a/trytond/modules/__init__.py +++ b/trytond/modules/__init__.py @@ -215,7 +215,8 @@ def load_module_graph(graph, pool, update=None, lang=None): lang.add(code) code = get_parent_language(code) - with Transaction().connection.cursor() as cursor: + transaction = Transaction() + with transaction.connection.cursor() as cursor: modules = [x.name for x in graph] cursor.execute(*ir_module.select(ir_module.name, ir_module.state, where=ir_module.name.in_(modules))) @@ -296,10 +297,17 @@ def load_module_graph(graph, pool, update=None, lang=None): ])) module2state[package.name] = 'activated' - Transaction().connection.commit() + transaction.commit() if not update: pool.setup() + else: + # Remove unknown models and fields + Model = pool.get('ir.model') + Model.clean() + ModelField = pool.get('ir.model.field') + ModelField.clean() + transaction.commit() pool.setup_mixin(modules)