From cd9c69bd7d9b21e6220a49f88f22160de33c5e06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Voet=20=28ryv=29?= Date: Tue, 23 Apr 2024 10:55:36 +0200 Subject: [PATCH] [FIX] core: fix non-attachment binary fields for web_save When bin_size=True is in the context, a computed non-attachment binary is incorrectly saved to the database. The row is actually updated with the size of the binary instead of the value itself. This commit fixes the problem by avoiding setting the cache with the bin_size value as dirty. Moreover, the binary size is computed with `pg_size_pretty` for non-attachment binary fields. Also, method compute_value() calls b64decode() on the value that was previously encoded in base64 by _compute_datas(). But _compute_datas() is specific to attachments, and is not used in this case. Thus b64decode() doesn't make sense. These 3 bugs are now covered by testing web_save(), where cache consistency is required. It was first reported for this method. Closes #156673 closes odoo/odoo#160708 Signed-off-by: Raphael Collet Co-authored-by: Renaud Thiry --- .../test_new_api/models/test_new_api.py | 5 ++- .../test_new_api/tests/test_web_save.py | 38 +++++++++++++++++++ odoo/fields.py | 12 +++--- 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/odoo/addons/test_new_api/models/test_new_api.py b/odoo/addons/test_new_api/models/test_new_api.py index 45398e74df6..a574b5b8514 100644 --- a/odoo/addons/test_new_api/models/test_new_api.py +++ b/odoo/addons/test_new_api/models/test_new_api.py @@ -941,7 +941,10 @@ class BinarySvg(models.Model): name = fields.Char(required=True) image_attachment = fields.Binary(attachment=True) image_wo_attachment = fields.Binary(attachment=False) - + image_wo_attachment_related = fields.Binary( + "image wo attachment", related="image_wo_attachment", + store=True, attachment=False, + ) class MonetaryBase(models.Model): _name = 'test_new_api.monetary_base' diff --git a/odoo/addons/test_new_api/tests/test_web_save.py b/odoo/addons/test_new_api/tests/test_web_save.py index 7d17e61848c..41678a61a40 100644 --- a/odoo/addons/test_new_api/tests/test_web_save.py +++ b/odoo/addons/test_new_api/tests/test_web_save.py @@ -1,6 +1,8 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from odoo.tests.common import TransactionCase +from odoo.addons.base.tests.test_mimetypes import SVG, JPG + class TestWebSave(TransactionCase): @@ -36,3 +38,39 @@ class TestWebSave(TransactionCase): # Modify an existing record, with unity specification result = person.web_save({'name': 'lpe'}, {'display_name': {}}) self.assertEqual(result, [{'id': person.id, 'display_name': 'lpe'}]) + + def test_web_save_computed_stored_binary(self): + [result] = self.env['test_new_api.binary_svg'].web_save( + {'name': 'test', 'image_wo_attachment': SVG}, + {'image_wo_attachment': {}, 'image_wo_attachment_related': {}}, + ) + self.assertEqual(result['image_wo_attachment'], '400 bytes') # From PostgreSQL + self.assertEqual(result['image_wo_attachment_related'], b'400.00 bytes') # From human_size + + # check cache values + record = self.env['test_new_api.binary_svg'].browse(result['id']) + self.assertEqual(record.image_wo_attachment, SVG) + self.assertEqual(record.image_wo_attachment, record.image_wo_attachment_related) + + # check database values + self.env.invalidate_all() + self.assertEqual(record.image_wo_attachment, SVG) + self.assertEqual(record.image_wo_attachment, record.image_wo_attachment_related) + + # check web_save() on existing record + self.env.invalidate_all() + [result] = record.web_save( + {'image_wo_attachment': JPG}, + {'image_wo_attachment': {}, 'image_wo_attachment_related': {}}, + ) + self.assertEqual(result['image_wo_attachment'], '727 bytes') # From PostgreSQL + self.assertEqual(result['image_wo_attachment_related'], b'727.00 bytes') # From human_size + + # check cache values + self.assertEqual(record.image_wo_attachment, JPG.encode()) + self.assertEqual(record.image_wo_attachment, record.image_wo_attachment_related) + + # check database values + self.env.invalidate_all() + self.assertEqual(record.image_wo_attachment, JPG.encode()) + self.assertEqual(record.image_wo_attachment, record.image_wo_attachment_related) diff --git a/odoo/fields.py b/odoo/fields.py index 86af1dea939..5a1943064c6 100644 --- a/odoo/fields.py +++ b/odoo/fields.py @@ -2406,18 +2406,18 @@ class Binary(Field): for record_no_bin_size, record in zip(records_no_bin_size, records): try: value = cache.get(record_no_bin_size, self) - try: - value = base64.b64decode(value) - except (TypeError, binascii.Error): - pass + # don't decode non-attachments to be consistent with pg_size_pretty + if not (self.store and self.column_type): + with contextlib.suppress(TypeError, binascii.Error): + value = base64.b64decode(value) try: if isinstance(value, (bytes, _BINARY)): value = human_size(len(value)) except (TypeError): pass cache_value = self.convert_to_cache(value, record) - dirty = self.column_type and self.store and any(records._ids) - cache.set(record, self, cache_value, dirty=dirty) + # the dirty flag is independent from this assignment + cache.set(record, self, cache_value, check_dirty=False) except CacheMiss: pass else: