diff --git a/openerp/modules/loading.py b/openerp/modules/loading.py index be94697ad1d..82980e2a252 100644 --- a/openerp/modules/loading.py +++ b/openerp/modules/loading.py @@ -29,7 +29,8 @@ _logger = logging.getLogger(__name__) _test_logger = logging.getLogger('openerp.tests') -def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=None, report=None): +def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=None, report=None, + models_to_check=None): """Migrates+Updates or Installs all module nodes from ``graph`` :param graph: graph of module nodes to load :param status: deprecated parameter, unused, left to avoid changing signature in 8.0 @@ -100,6 +101,9 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules= if kind in ('demo', 'test'): threading.currentThread().testing = False + if models_to_check is None: + models_to_check = set() + processed_modules = [] loaded_modules = [] registry = openerp.registry(cr.dbname) @@ -112,6 +116,8 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules= t0 = time.time() t0_sql = openerp.sql_db.sql_counter + models_updated = set() + for index, package in enumerate(graph): module_name = package.name module_id = package.id @@ -130,11 +136,22 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules= getattr(py_module, pre_init)(cr) models = registry.load(cr, package) + model_names = [m._name for m in models] loaded_modules.append(package.name) - if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'): + if (hasattr(package, 'init') or hasattr(package, 'update') + or package.state in ('to install', 'to upgrade')): + models_updated |= set(model_names) + models_to_check -= set(model_names) registry.setup_models(cr, partial=True) init_module_models(cr, package.name, models) + elif package.state != 'to remove': + # The current module has simply been loaded. The models extended by this module + # and for which we updated the schema, must have their schema checked again. + # This is because the extension may have changed the model, + # e.g. adding required=True to an existing field, but the schema has not been + # updated by this module because it's not marked as 'to upgrade/to install'. + models_to_check |= set(model_names) & models_updated idref = {} @@ -225,9 +242,14 @@ def _check_module_names(cr, module_names): incorrect_names = mod_names.difference([x['name'] for x in cr.dictfetchall()]) _logger.warning('invalid module names, ignored: %s', ", ".join(incorrect_names)) -def load_marked_modules(cr, graph, states, force, progressdict, report, loaded_modules, perform_checks): +def load_marked_modules(cr, graph, states, force, progressdict, report, loaded_modules, + perform_checks, models_to_check=None): """Loads modules marked with ``states``, adding them to ``graph`` and ``loaded_modules`` and returns a list of installed/upgraded modules.""" + + if models_to_check is None: + models_to_check = set() + processed_modules = [] while True: cr.execute("SELECT name from ir_module_module WHERE state IN %s" ,(tuple(states),)) @@ -236,7 +258,10 @@ def load_marked_modules(cr, graph, states, force, progressdict, report, loaded_m break graph.add_modules(cr, module_list, force) _logger.debug('Updating graph with %d more modules', len(module_list)) - loaded, processed = load_module_graph(cr, graph, progressdict, report=report, skip_modules=loaded_modules, perform_checks=perform_checks) + loaded, processed = load_module_graph( + cr, graph, progressdict, report=report, skip_modules=loaded_modules, + perform_checks=perform_checks, models_to_check=models_to_check + ) processed_modules.extend(processed) loaded_modules.extend(loaded) if not processed: @@ -250,6 +275,8 @@ def load_modules(db, force_demo=False, status=None, update_module=False): if force_demo: force.append('demo') + models_to_check = set() + cr = db.cursor() try: if not openerp.modules.db.is_initialized(cr): @@ -278,7 +305,10 @@ def load_modules(db, force_demo=False, status=None, update_module=False): # processed_modules: for cleanup step after install # loaded_modules: to avoid double loading report = registry._assertion_report - loaded_modules, processed_modules = load_module_graph(cr, graph, status, perform_checks=update_module, report=report) + loaded_modules, processed_modules = load_module_graph( + cr, graph, status, perform_checks=update_module, + report=report, models_to_check=models_to_check + ) load_lang = tools.config.pop('load_language') if load_lang or update_module: @@ -333,11 +363,11 @@ def load_modules(db, force_demo=False, status=None, update_module=False): previously_processed = len(processed_modules) processed_modules += load_marked_modules(cr, graph, ['installed', 'to upgrade', 'to remove'], - force, status, report, loaded_modules, update_module) + force, status, report, loaded_modules, update_module, models_to_check) if update_module: processed_modules += load_marked_modules(cr, graph, ['to install'], force, status, report, - loaded_modules, update_module) + loaded_modules, update_module, models_to_check) registry.setup_models(cr) @@ -398,6 +428,17 @@ def load_modules(db, force_demo=False, status=None, update_module=False): openerp.api.Environment.reset() return openerp.modules.registry.RegistryManager.new(cr.dbname, force_demo, status, update_module) + # STEP 5.5: Verify extended fields on every model + # This will fix the schema of all models in a situation such as: + # - module A is loaded and defines model M; + # - module B is installed/upgraded and extends model M; + # - module C is loaded and extends model M; + # - module B and C depend on A but not on each other; + # The changes introduced by module C are not taken into account by the upgrade of B. + if models_to_check: + module_objs_to_check = [registry.models[m] for m in models_to_check] + init_module_models(cr, 'verification in progress', module_objs_to_check) + # STEP 6: verify custom views on every model if update_module: Views = registry['ir.ui.view']