From 35e22b8fe906d26618ebbc02583f980f7fa20e42 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Mon, 30 Oct 2023 09:41:56 +0100 Subject: [PATCH] [IMP] core: reflect the model inherits in the database This will help the upgrade process. Currently, there is an harcoded[^1] inherit tree stored in the upgrade source. By storing this information in the databases, we: - avoid this hardcoded list. - support more than the standard modules. task-3504720 [^1]: hardcoded, but autogenerated. closes odoo/odoo#140261 Signed-off-by: Raphael Collet --- odoo/addons/base/models/ir_model.py | 88 +++++++++++++++++++ odoo/addons/base/security/ir.model.access.csv | 1 + odoo/addons/base/tests/test_ir_actions.py | 2 +- odoo/modules/registry.py | 1 + 4 files changed, 91 insertions(+), 1 deletion(-) diff --git a/odoo/addons/base/models/ir_model.py b/odoo/addons/base/models/ir_model.py index 0a0671ab9ce..efe8735564d 100644 --- a/odoo/addons/base/models/ir_model.py +++ b/odoo/addons/base/models/ir_model.py @@ -1319,6 +1319,94 @@ class IrModelFields(models.Model): field = self._get(model_name, field_name) return [(sel.value, sel.name) for sel in field.selection_ids] + +class ModelInherit(models.Model): + _name = "ir.model.inherit" + _description = "Model Inheritance Tree" + _log_access = False + + model_id = fields.Many2one("ir.model", required=True, ondelete="cascade") + parent_id = fields.Many2one("ir.model", required=True, ondelete="cascade") + parent_field_id = fields.Many2one("ir.model.fields", ondelete="cascade") # in case of inherits + + _sql_constraints = [ + ("uniq", "UNIQUE(model_id, parent_id)", "Models inherits from another only once") + ] + + def _reflect_inherits(self, model_names): + """ Reflect the given models' inherits (_inherit and _inherits). """ + IrModel = self.env["ir.model"] + get_model_id = IrModel._get_id + + module_mapping = defaultdict(list) + for model_name in model_names: + get_field_id = self.env["ir.model.fields"]._get_ids(model_name).get + model_id = get_model_id(model_name) + model = self.env[model_name] + + for cls in reversed(type(model).mro()): + if not models.is_definition_class(cls): + continue + + items = [ + (model_id, get_model_id(parent_name), None) + for parent_name in cls._inherit + if parent_name not in ("base", model_name) + ] + [ + (model_id, get_model_id(parent_name), get_field_id(field)) + for parent_name, field in cls._inherits.items() + ] + + for item in items: + module_mapping[item].append(cls._module) + + if not module_mapping: + return + + cr = self.env.cr + cr.execute( + """ + SELECT i.id, i.model_id, i.parent_id, i.parent_field_id + FROM ir_model_inherit i + JOIN ir_model m + ON m.id = i.model_id + WHERE m.model IN %s + """, + [tuple(model_names)] + ) + existing = {} + inh_ids = {} + for iid, model_id, parent_id, parent_field_id in cr.fetchall(): + inh_ids[(model_id, parent_id, parent_field_id)] = iid + existing[(model_id, parent_id)] = parent_field_id + + sentinel = object() + cols = ["model_id", "parent_id", "parent_field_id"] + rows = [item for item in module_mapping if existing.get(item[:2], sentinel) != item[2]] + if rows: + ids = upsert_en(self, cols, rows, ["model_id", "parent_id"]) + for row, id_ in zip(rows, ids): + inh_ids[row] = id_ + self.pool.post_init(mark_modified, self.browse(ids), cols[1:]) + + # update their XML id + IrModel.browse(id_ for item in module_mapping for id_ in item[:2]).fetch(['model']) + data_list = [] + for (model_id, parent_id, parent_field_id), modules in module_mapping.items(): + model_name = IrModel.browse(model_id).model.replace(".", "_") + parent_name = IrModel.browse(parent_id).model.replace(".", "_") + record_id = inh_ids[(model_id, parent_id, parent_field_id)] + data_list += [ + { + "xml_id": f"{module}.model_inherit__{model_name}__{parent_name}", + "record": self.browse(record_id), + } + for module in modules + ] + + self.env["ir.model.data"]._update_xmlids(data_list) + + class IrModelSelection(models.Model): _name = 'ir.model.fields.selection' _order = 'sequence, id' diff --git a/odoo/addons/base/security/ir.model.access.csv b/odoo/addons/base/security/ir.model.access.csv index 49b93a2d352..fb2af6731bc 100644 --- a/odoo/addons/base/security/ir.model.access.csv +++ b/odoo/addons/base/security/ir.model.access.csv @@ -9,6 +9,7 @@ "access_ir_model_group_erp_manager","ir_model group_erp_manager","model_ir_model","group_erp_manager",1,1,1,1 "access_ir_model_constraint_group_erp_manager","ir_model_constraint group_erp_manager","model_ir_model_constraint","group_erp_manager",1,1,1,1 "access_ir_model_relation_group_erp_manager","ir_model_relation group_erp_manager","model_ir_model_relation","group_erp_manager",1,1,1,1 +"access_ir_model_inherit","ir_model_inherit nobody","model_ir_model_inherit",,0,0,0,0 "access_ir_model_access_group_erp_manager","ir_model_access_group_erp_manager","model_ir_model_access","group_erp_manager",1,1,1,1 "access_ir_model_data_group_erp_manager","ir_model_data group_erp_manager","model_ir_model_data","group_erp_manager",1,1,1,1 "access_ir_model_fields_group_erp_manager","ir_model_fields group_erp_manager","model_ir_model_fields","group_erp_manager",1,1,1,1 diff --git a/odoo/addons/base/tests/test_ir_actions.py b/odoo/addons/base/tests/test_ir_actions.py index 3c6fcc5cea9..0daab245877 100644 --- a/odoo/addons/base/tests/test_ir_actions.py +++ b/odoo/addons/base/tests/test_ir_actions.py @@ -735,7 +735,7 @@ class TestCustomFields(TestCommonCustomFields): # create a non-computed field, and assert how many queries it takes model_id = self.env['ir.model']._get_id('res.partner') - query_count = 38 + query_count = 44 with self.assertQueryCount(query_count): self.env.registry.clear_cache() self.env['ir.model.fields'].create({ diff --git a/odoo/modules/registry.py b/odoo/modules/registry.py index 4b048c17074..568ae14f926 100644 --- a/odoo/modules/registry.py +++ b/odoo/modules/registry.py @@ -573,6 +573,7 @@ class Registry(Mapping): env['ir.model.fields']._reflect_fields(model_names) env['ir.model.fields.selection']._reflect_selections(model_names) env['ir.model.constraint']._reflect_constraints(model_names) + env['ir.model.inherit']._reflect_inherits(model_names) self._ordinary_tables = None