This module allows filtering values to update on records according to
+whether they are actually different from the records’ current values.
+
+
Summary
+
This module allows you to update records by filtering out fields whose
+values are going to be left unchanged by BaseModel.write(); for
+example, let’s assume you have:
+
+>>> self
+sale.order.line(1,)
+>>> self.price_unit
+10.00
+
+
If you use self.write({"price_unit": 10.00}) or
+self.price_unit = 10.00, Odoo may end up executing unnecessary
+operations, like triggering the update on the field, recompute computed
+fields that depend on price_unit, and so on, even if the value is
+actually unchanged.
+
By using this module, you can prevent all of that.
+
You can use this module in 3 different ways. All of them require you to
+add this module as a dependency of your module.
+
1 - Context key ``”write_use_diff_values”``
+
By adding write_use_diff_values=True to the context when updating a
+field value, the BaseModel.write() patch will take care of filtering
+out the fields’ values that are the same as the record’s current ones.
+
⚠️ Beware: the context key is propagated down to other write() calls
+
Example:
+
+from odoo import models
+
+
+class ProductTemplate(models.Model):
+ _inherit = "product.template"
+
+ def write(self, vals):
+ # Update only fields that are actually different
+ self = self.with_context(write_use_diff_values=True)
+ return super().write(vals)
+
+
+class ProductProduct(models.Model):
+ _inherit = "product.product"
+
+ def update_code_if_necessary(self, code: str):
+ # Update ``default_code`` only if different from the current value
+ self.with_context(write_use_diff_values=True).default_code = code
+
+
2 - Method ``BaseModel.write_diff()``
+
It is the same as calling write(), but it automatically enables the
+"write_use_diff_values" context flag: self.write_diff(vals) is a
+shortcut for
+self.with_context(write_use_diff_values=True).write(vals)
+
⚠️ Beware: the context key is propagated down to other write() calls
+
3 - Method ``BaseModel._get_write_diff_values(vals)``
+
This method accepts a write-like dict as param, and returns a new
+dict made of the fields who will actually update the record’s
+values. This allows for a more flexible and customizable behavior than
+the context key usage, because:
+
+- you’ll be able to filter out specific fields, instead of filtering out
+all the fields whose values won’t be changed after the update;
+- you’ll be able to execute the filtering on specific models, instead of
+executing it on all the models involved in the stack of write()
+calls from the first usage of the context key down to the base method
+BaseModel.write().
+
+
Example:
+
+from collections import defaultdict
+
+from odoo import api, models
+from odoo.tools.misc import frozendict
+
+
+class ProductProduct(models.Model):
+ _inherit = "product.product"
+
+ def write(self, vals):
+ # OVERRIDE: ``odoo.addons.product.models.product_product.Product.write()``
+ # override will clear the whole registry cache if either 'active' or
+ # 'product_template_attribute_value_ids' are found in the ``vals`` dictionary:
+ # remove them unless it's necessary to update them
+ fnames = {"active", "product_template_attribute_value_ids"}
+ if vals_to_check := {f: vals.pop(f) for f in fnames.intersection(vals)}:
+ groups = defaultdict(lambda: self.browse())
+ for prod in self:
+ groups[frozendict(prod._get_write_diff_values(vals_to_check))] += prod
+ for diff_vals, prods in groups.items():
+ if res_vals := (vals | dict(diff_vals)):
+ super(ProductProduct, prods).write(res_vals)
+ return True
+ return super().write(vals)
+
+