From d978d47eba57ce752701aa6bded1956848afcb68 Mon Sep 17 00:00:00 2001 From: Edoardo Piroli Date: Fri, 20 Feb 2026 16:55:05 +0100 Subject: [PATCH 1/2] [IMP] util.update_record_from_xml Sometimes records get a field removed from their XML declaration. This creates a divergence between new dbs and upgraded ones. The former will be initialised with `NULL`/default values, whereas the latter will retain their current one. This is usually handled in dedicated upgrade scripts with simple queries un/setting the necessary columns. However, in some cases, these XML changes are backported/noticed too late and need to be addressed in multiple versions. That may create the pressure to target future versions, which creates a hidden coupling between the xml declaration and the query, prone to turn into a bug. It is one such example[^1] that inspired this PR. Here `update_record_from_xml` is adapted to unset fields missing from the xml declaration, if passed explicitely via the `fields` kwarg. This enables us to leverage the foreward compatibility of `update_record_from_xml` in the scenario described above, preventing the associated potential bugs. [^1]: https://github.com/odoo/upgrade/pull/9512 --- src/util/records.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/util/records.py b/src/util/records.py index 094ebe86b..cd33d2ed7 100644 --- a/src/util/records.py +++ b/src/util/records.py @@ -1039,7 +1039,8 @@ def update_record_from_xml( should also be updated. :param set(str) or None fields: optional list of fields to include in the XML declaration. If set, all other fields will be ignored. When set, record - won't be created if missing. + won't be created if missing and all fields not found in + the XML declaration will be set to NULL. .. warning:: This functions uses the ORM, therefore it can only be used after **all** models @@ -1147,9 +1148,13 @@ def add_ref(ref): found = True parent = node.getparent() if node.tag == "record" and fields is not None: + xml_fields = set() for fn in node.xpath("./field[@name]"): if fn.attrib["name"] not in fields: node.remove(fn) + xml_fields.add(fn.attrib["name"]) + # override requested fields not found in xml + node.extend(lxml.builder.E.field(name=f, eval="False") for f in fields if f not in xml_fields) new_root[0].append(node) if node.tag == "menuitem" and parent.tag == "menuitem" and "parent_id" not in node.attrib: From e56d99ae998bac6e4fc7c2623341e92d772adcd6 Mon Sep 17 00:00:00 2001 From: Edoardo Piroli Date: Tue, 24 Feb 2026 15:09:55 +0100 Subject: [PATCH 2/2] fixup! [IMP] util.update_record_from_xml --- src/util/records.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/util/records.py b/src/util/records.py index cd33d2ed7..a5601bd0f 100644 --- a/src/util/records.py +++ b/src/util/records.py @@ -1154,7 +1154,15 @@ def add_ref(ref): node.remove(fn) xml_fields.add(fn.attrib["name"]) # override requested fields not found in xml - node.extend(lxml.builder.E.field(name=f, eval="False") for f in fields if f not in xml_fields) + fields_default = env(cr)[node.attrib["model"]].default_get(fields) + for field in fields: + if field not in xml_fields: + field_default = fields_default.get(field) + if field_default is None: + node.append(lxml.builder.E.field(name=field, eval="False")) + else: + node.append(lxml.builder.E.field(str(field_default), name=field)) + new_root[0].append(node) if node.tag == "menuitem" and parent.tag == "menuitem" and "parent_id" not in node.attrib: