Skip to content
Permalink
Browse files

[FIX] product: clear caches less aggressively

Follow up of 5b32f1b

We now only invalidate what has to be invalidated.

Also ormcache does not expect a recordset as parameter.
  • Loading branch information...
seb-odoo committed Apr 18, 2019
1 parent df68900 commit b9f9c347324f2b55b4bf7a93d2097ffd5ec13f42
@@ -345,23 +345,59 @@ def create(self, vals_list):
product._set_standard_price(vals.get('standard_price') or 0.0)
# `_get_variant_id_for_combination` depends on existing variants
self.clear_caches()
self.env['product.template'].invalidate_cache(
fnames=[
'product_variant_ids',
'product_variant_id',
'product_variant_count',
'valid_archived_variant_ids',
'valid_existing_variant_ids',
],
ids=products.mapped('product_tmpl_id').ids
)
return products

@api.multi
def write(self, values):
''' Store the standard price change in order to be able to retrieve the cost of a product for a given date'''
''' Store the standard price change in order to be able to retrieve the cost of a product for a given date.
Also clear caches when appropriate.
'''
existing_attribute_value_ids = []
to_invalidate = self.env['product.template']

# avoid testing it the loop, for performance
pav_in_values = 'attribute_value_ids' in values
active_in_values = 'active' in values

# only loop if needed, for performance
if pav_in_values or active_in_values:
for product in self:
if pav_in_values:
existing_attribute_value_ids.append(product.attribute_value_ids)

if active_in_values and product.active != values['active']:
to_invalidate |= product

res = super(ProductProduct, self).write(values)

if 'standard_price' in values:
self._set_standard_price(values['standard_price'])
if 'attribute_value_ids' in values:
# `_get_variant_id_for_combination` depends on `attribute_value_ids`
self.clear_caches()
if 'active' in values:
# prefetched o2m have to be reloaded (because of active_test)
# (eg. product.template: product_variant_ids)
self.invalidate_cache()

if to_invalidate:
self.env['product.template'].invalidate_cache(fnames=[
'product_variant_ids',
'product_variant_id',
'product_variant_count',
'valid_archived_variant_ids',
'valid_existing_variant_ids',
], ids=to_invalidate.mapped('product_tmpl_id').ids)

if to_invalidate or any(attribute_value_ids != p.attribute_value_ids for attribute_value_ids, p in pycompat.izip(existing_attribute_value_ids, self)):
# `_get_first_possible_variant_id` depends on variants active state
# `_get_variant_id_for_combination` depends on `attribute_value_ids`
self.clear_caches()

return res

@api.multi
@@ -38,18 +38,33 @@ def write(self, vals):
This is important to prevent because changing the type would make
existing combinations invalid without recomputing them, and recomputing
them might take too long and we don't want to change products without
the user knowing about it."""
the user knowing about it.
We also need to invalidate the cache when changing the sequence because
the prefeteched o2m `attribute_line_ids` has to be resequenced.
"""
if 'create_variant' in vals:
products = self._get_related_product_templates()
if products:
message = ', '.join(products.mapped('name'))
raise UserError(_('You are trying to change the type of an attribute value still referenced on at least one product template: %s') % message)
invalidate_cache = 'sequence' in vals and any(record.sequence != vals['sequence'] for record in self)

to_invalidate = self.filtered(lambda pa: pa.sequence != vals['sequence']) if 'sequence' in vals else self.env['product.attribute']

res = super(ProductAttribute, self).write(vals)
if invalidate_cache:
# prefetched o2m have to be resequenced
# (eg. product.template: attribute_line_ids)
self.invalidate_cache()

if to_invalidate:
self.env['product.attribute'].invalidate_cache(fnames=['attribute_line_ids'], ids=to_invalidate.ids)
self.env['product.template'].invalidate_cache(fnames=[
'attribute_line_ids',
'valid_product_template_attribute_line_ids',
'valid_product_template_attribute_line_wnva_ids',
'valid_product_attribute_value_ids',
'valid_product_attribute_value_wnva_ids',
'valid_product_attribute_ids',
'valid_product_attribute_wnva_ids',
], ids=to_invalidate.mapped('attribute_line_ids.product_tmpl_id').ids)

return res

@api.multi
@@ -86,12 +101,18 @@ def _variant_name(self, variable_attributes):

@api.multi
def write(self, values):
invalidate_cache = 'sequence' in values and any(record.sequence != values['sequence'] for record in self)
to_invalidate = self.filtered(lambda pav: pav.sequence != values['sequence']) if 'sequence' in values else self.env['product.attribute.value']
res = super(ProductAttributeValue, self).write(values)
if invalidate_cache:
# prefetched o2m have to be resequenced
# (eg. product.template.attribute.line: value_ids)
self.invalidate_cache()
if to_invalidate:
lines = self.env['product.template.attribute.line'].search([('value_ids', 'in', to_invalidate.ids)])
self.env['product.template.attribute.line'].invalidate_cache(fnames=[
'value_ids',
'product_template_value_ids',
], ids=lines.ids)
self.env['product.template'].invalidate_cache(fnames=[
'valid_product_attribute_value_ids',
'valid_product_attribute_value_wnva_ids',
], ids=lines.mapped('product_tmpl_id').ids)
return res

@api.multi
@@ -518,8 +518,10 @@ def _onchange_compute_price(self):

@api.multi
def write(self, values):
invalidate_cache = any(any(record[key] != value for record in self) for key, value in values.items())
res = super(PricelistItem, self).write(values)
# When the pricelist changes we need the product.template price
# to be invalided and recomputed.
self.invalidate_cache()
if invalidate_cache:
# When the pricelist changes we need the product.template price
# to be invalided and recomputed.
self.invalidate_cache()
return res
@@ -549,10 +549,14 @@ def create_variant_ids(self):
# This is the case from existing stock reordering rules.
variant.write({'active': False})

# prefetched o2m have to be reloaded (because of active_test)
# (eg. product.template: product_variant_ids)
# We can't rely on existing invalidate_cache because of the savepoint.
self.invalidate_cache()
self.env['product.template'].invalidate_cache(fnames=[
'product_variant_ids',
'product_variant_id',
'product_variant_count',
'valid_archived_variant_ids',
'valid_existing_variant_ids',
], ids=self.ids)
return True

def has_dynamic_attributes(self):
@@ -873,7 +877,7 @@ def _get_variant_for_combination(self, combination):
return self.env['product.product'].browse(self._get_variant_id_for_combination(attribute_values))

@api.multi
@tools.ormcache('self', 'attribute_values')
@tools.ormcache('self.id', 'attribute_values.ids')
def _get_variant_id_for_combination(self, attribute_values):
"""See `_get_variant_for_combination`. This method returns an ID
so it can be cached."""
@@ -895,7 +899,7 @@ def _get_variant_id_for_combination(self, attribute_values):
)[:1].id

@api.multi
@tools.ormcache('self')
@tools.ormcache('self.id')
def _get_first_possible_variant_id(self):
"""See `_create_first_product_variant`. This method returns an ID
so it can be cached."""

0 comments on commit b9f9c34

Please sign in to comment.
You can’t perform that action at this time.