summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaphael Collet <rco@odoo.com>2019-10-15 14:07:39 +0000
committerfw-bot <otis@odoo.com>2019-10-21 09:08:10 +0000
commit883fd9e045350512c437fe8e518f08e3b880b511 (patch)
tree954f592b9eba3303e8561735605d4f34b1dde5e6
parentdb90ae1cd6b7417d17f0af076d9ec1987d94b509 (diff)
[FIX] fields: ensure Binary are never recomputed with "bin_size"
The compute methods are always expecting the real value and not the bin_size. The solution is to always compute with `bin_size=False`, and then manually compute the `bin_size` and set it on the cache of `bin_size=True`. PR: #38292 Co-authored-by Sébastien Theys <seb@odoo.com> X-original-commit: 7744886d6141ca7971d91807d0444c707e10fdf8
-rw-r--r--odoo/addons/test_new_api/ir.model.access.csv1
-rw-r--r--odoo/addons/test_new_api/models.py9
-rw-r--r--odoo/addons/test_new_api/tests/test_new_fields.py58
-rw-r--r--odoo/fields.py33
4 files changed, 97 insertions, 4 deletions
diff --git a/odoo/addons/test_new_api/ir.model.access.csv b/odoo/addons/test_new_api/ir.model.access.csv
index 9d984b42652..b676e85dd0d 100644
--- a/odoo/addons/test_new_api/ir.model.access.csv
+++ b/odoo/addons/test_new_api/ir.model.access.csv
@@ -44,3 +44,4 @@ access_test_new_api_display,access_test_new_api_display,model_test_new_api_displ
access_test_new_api_model_active_field,access_test_new_api_model_active_field,model_test_new_api_model_active_field,,1,1,1,1
access_test_new_api_model_many2one_reference,access_test_new_api_model_many2one_reference,model_test_new_api_model_many2one_reference,,1,1,1,1
access_test_new_api_inverse_m2o_ref,access_test_new_api_inverse_m2o_ref,model_test_new_api_inverse_m2o_ref,,1,1,1,1
+access_test_new_api_model_binary,access_test_new_api_model_binary,model_test_new_api_model_binary,,1,1,1,1
diff --git a/odoo/addons/test_new_api/models.py b/odoo/addons/test_new_api/models.py
index d4176ffa722..a6ce5fe2e37 100644
--- a/odoo/addons/test_new_api/models.py
+++ b/odoo/addons/test_new_api/models.py
@@ -517,6 +517,15 @@ class ComputeOnchange(models.Model):
record.baz = record.foo
+class ModelBinary(models.Model):
+ _name = 'test_new_api.model_binary'
+ _description = 'Test Image field'
+
+ binary = fields.Binary()
+ binary_related_store = fields.Binary("Binary Related Store", related='binary', store=True, readonly=False)
+ binary_related_no_store = fields.Binary("Binary Related No Store", related='binary', store=False, readonly=False)
+
+
class ModelImage(models.Model):
_name = 'test_new_api.model_image'
_description = 'Test Image field'
diff --git a/odoo/addons/test_new_api/tests/test_new_fields.py b/odoo/addons/test_new_api/tests/test_new_fields.py
index e326d8575b1..805688f45ed 100644
--- a/odoo/addons/test_new_api/tests/test_new_fields.py
+++ b/odoo/addons/test_new_api/tests/test_new_fields.py
@@ -1497,6 +1497,64 @@ class TestFields(common.TransactionCase):
self.assertEqual(Image.open(io.BytesIO(base64.b64decode(record.image))).size, (2000, 4000))
self.assertEqual(Image.open(io.BytesIO(base64.b64decode(record.image_256))).size, (128, 256))
+ # test bin_size
+ record_bin_size = record.with_context(bin_size=True)
+ self.assertEqual(record_bin_size.image, b'31.54 Kb')
+ self.assertEqual(record_bin_size.image_512, b'1.02 Kb')
+ self.assertEqual(record_bin_size.image_256, b'424.00 bytes')
+
+ def test_95_binary_bin_size(self):
+ binary_value = base64.b64encode(b'content')
+ binary_size = b'7.00 bytes'
+
+ def assertBinaryValue(record, value):
+ for field in ('binary', 'binary_related_store', 'binary_related_no_store'):
+ self.assertEqual(record[field], value)
+
+ # created, flushed, and first read without context
+ record = self.env['test_new_api.model_binary'].create({'binary': binary_value})
+ record.flush()
+ record.invalidate_cache()
+ record_no_bin_size = record.with_context(bin_size=False)
+ record_bin_size = record.with_context(bin_size=True)
+
+ assertBinaryValue(record, binary_value)
+ assertBinaryValue(record_no_bin_size, binary_value)
+ assertBinaryValue(record_bin_size, binary_size)
+
+ # created, flushed, and first read with bin_size=False
+ record_no_bin_size = self.env['test_new_api.model_binary'].with_context(bin_size=False).create({'binary': binary_value})
+ record_no_bin_size.flush()
+ record_no_bin_size.invalidate_cache()
+ record = self.env['test_new_api.model_binary'].browse(record.id)
+ record_bin_size = record.with_context(bin_size=True)
+
+ assertBinaryValue(record_no_bin_size, binary_value)
+ assertBinaryValue(record, binary_value)
+ assertBinaryValue(record_bin_size, binary_size)
+
+ # created, flushed, and first read with bin_size=True
+ record_bin_size = self.env['test_new_api.model_binary'].with_context(bin_size=True).create({'binary': binary_value})
+ record_bin_size.flush()
+ record_bin_size.invalidate_cache()
+ record = self.env['test_new_api.model_binary'].browse(record.id)
+ record_no_bin_size = record.with_context(bin_size=False)
+
+ assertBinaryValue(record_bin_size, binary_size)
+ assertBinaryValue(record_no_bin_size, binary_value)
+ assertBinaryValue(record, binary_value)
+
+ # created without context and flushed with bin_size
+ record = self.env['test_new_api.model_binary'].create({'binary': binary_value})
+ record_no_bin_size = record.with_context(bin_size=False)
+ record_bin_size = record.with_context(bin_size=True)
+ record_bin_size.flush()
+ record_bin_size.invalidate_cache()
+
+ assertBinaryValue(record, binary_value)
+ assertBinaryValue(record_no_bin_size, binary_value)
+ assertBinaryValue(record_bin_size, binary_size)
+
class TestX2many(common.TransactionCase):
def test_definition_many2many(self):
diff --git a/odoo/fields.py b/odoo/fields.py
index b757beae734..4abdc9dcf1f 100644
--- a/odoo/fields.py
+++ b/odoo/fields.py
@@ -3,15 +3,13 @@
""" High-level objects for fields. """
-from collections import OrderedDict, defaultdict
+from collections import defaultdict
from datetime import date, datetime, time
-from dateutil.relativedelta import relativedelta
-from functools import partial
from operator import attrgetter
import itertools
import logging
import base64
-
+import binascii
import pytz
try:
@@ -28,6 +26,7 @@ from .tools import DEFAULT_SERVER_DATE_FORMAT as DATE_FORMAT
from .tools import DEFAULT_SERVER_DATETIME_FORMAT as DATETIME_FORMAT
from .tools.translate import html_translate, _
from .tools.mimetypes import guess_mimetype
+from odoo.exceptions import CacheMiss
DATE_LENGTH = len(date.today().strftime(DATE_FORMAT))
DATETIME_LENGTH = len(datetime.now().strftime(DATETIME_FORMAT))
@@ -1904,6 +1903,32 @@ class Binary(Field):
return bytes(value)
return False if value is None else value
+ def compute_value(self, records):
+ bin_size_name = 'bin_size_' + self.name
+ if records.env.context.get('bin_size') or records.env.context.get(bin_size_name):
+ # always compute without bin_size
+ records_no_bin_size = records.with_context(**{'bin_size': False, bin_size_name: False})
+ super().compute_value(records_no_bin_size)
+ # manually update the bin_size cache
+ cache = records.env.cache
+ 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
+ try:
+ value = human_size(len(value))
+ except (TypeError):
+ pass
+ cache_value = self.convert_to_cache(value, record)
+ cache.set(record, self, cache_value)
+ except CacheMiss:
+ pass
+ else:
+ super().compute_value(records)
+
def read(self, records):
# values are stored in attachments, retrieve them
assert self.attachment