Skip to content

Commit

Permalink
[FIX] core: cache inconsistency when assigning related resized image …
Browse files Browse the repository at this point in the history
…field

After writing or creating on a related Image field, its cache contains
the full-size image instead of the resized one (according to its
attributes max_width and max_height).  Fix the cache with the resized
image at the end of the inverse method.
  • Loading branch information
ryv-odoo authored and rco-odoo committed Apr 26, 2024
1 parent fe287f0 commit 96c5fe8
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 7 deletions.
2 changes: 1 addition & 1 deletion odoo/addons/test_new_api/models/test_new_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -931,7 +931,7 @@ class ModelImage(models.Model):
image_512 = fields.Image("Image 512", related='image', max_width=512, max_height=512, store=True, readonly=False)
image_256 = fields.Image("Image 256", related='image', max_width=256, max_height=256, store=False, readonly=False)
image_128 = fields.Image("Image 128", max_width=128, max_height=128)
image_64 = fields.Image("Image 64", related='image', max_width=64, max_height=64, store=True, attachment=False)
image_64 = fields.Image("Image 64", related='image', max_width=64, max_height=64, store=True, attachment=False, readonly=False)


class BinarySvg(models.Model):
Expand Down
22 changes: 18 additions & 4 deletions odoo/addons/test_new_api/tests/test_new_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -2653,7 +2653,6 @@ def test_94_image(self):
'name': 'image',
'image_512': image_w,
})
record.invalidate_recordset(['image_512'])
self.assertEqual(Image.open(io.BytesIO(base64.b64decode(record.image_512))).size, (512, 256))
self.assertEqual(Image.open(io.BytesIO(base64.b64decode(record.image))).size, (4000, 2000))
self.assertEqual(Image.open(io.BytesIO(base64.b64decode(record.image_256))).size, (256, 128))
Expand All @@ -2662,7 +2661,6 @@ def test_94_image(self):
record.write({
'image_512': image_h,
})
record.invalidate_recordset(['image_512'])
self.assertEqual(Image.open(io.BytesIO(base64.b64decode(record.image_512))).size, (256, 512))
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))
Expand All @@ -2673,7 +2671,6 @@ def test_94_image(self):
'name': 'image',
'image_256': image_w,
})
record.invalidate_recordset(['image_256'])
self.assertEqual(Image.open(io.BytesIO(base64.b64decode(record.image_512))).size, (512, 256))
self.assertEqual(Image.open(io.BytesIO(base64.b64decode(record.image))).size, (4000, 2000))
self.assertEqual(Image.open(io.BytesIO(base64.b64decode(record.image_256))).size, (256, 128))
Expand All @@ -2682,7 +2679,24 @@ def test_94_image(self):
record.write({
'image_256': image_h,
})
record.invalidate_recordset(['image_256'])
self.assertEqual(Image.open(io.BytesIO(base64.b64decode(record.image_512))).size, (256, 512))
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))
self.assertEqual(Image.open(io.BytesIO(base64.b64decode(record.image_64))).size, (32, 64))

# test create inverse stored column
record = self.env['test_new_api.model_image'].with_context(image_no_postprocess=True).create({
'name': 'image',
'image_64': image_w,
})
self.assertEqual(Image.open(io.BytesIO(base64.b64decode(record.image_512))).size, (512, 256))
self.assertEqual(Image.open(io.BytesIO(base64.b64decode(record.image))).size, (4000, 2000))
self.assertEqual(Image.open(io.BytesIO(base64.b64decode(record.image_256))).size, (256, 128))
self.assertEqual(Image.open(io.BytesIO(base64.b64decode(record.image_64))).size, (64, 32))
# test write inverse stored column
record.write({
'image_64': image_h,
})
self.assertEqual(Image.open(io.BytesIO(base64.b64decode(record.image_512))).size, (256, 512))
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))
Expand Down
15 changes: 13 additions & 2 deletions odoo/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -2538,10 +2538,11 @@ def setup(self, model):
def create(self, record_values):
new_record_values = []
for record, value in record_values:
# strange behavior when setting related image field, when `self`
# does not resize the same way as its related field
new_value = self._image_process(value, record.env)
new_record_values.append((record, new_value))
# when setting related image field, keep the unprocessed image in
# cache to let the inverse method use the original image; the image
# will be resized once the inverse has been applied
cache_value = self.convert_to_cache(value if self.related else new_value, record)
record.env.cache.update(record, self, itertools.repeat(cache_value))
super(Image, self).create(new_record_values)
Expand All @@ -2564,6 +2565,16 @@ def write(self, records, value):
dirty = self.column_type and self.store and any(records._ids)
records.env.cache.update(records, self, itertools.repeat(cache_value), dirty=dirty)

def _inverse_related(self, records):
super()._inverse_related(records)
if not (self.max_width and self.max_height):
return
# the inverse has been applied with the original image; now we fix the
# cache with the resized value
for record in records:
value = self._process_related(record[self.name], record.env)
record.env.cache.set(record, self, value, dirty=(self.store and self.column_type))

def _image_process(self, value, env):
if self.readonly and not self.max_width and not self.max_height:
# no need to process images for computed fields, or related fields
Expand Down

0 comments on commit 96c5fe8

Please sign in to comment.