[IMP] base: cache property.get(res_id=False)

After installing a bunch more modules including account & some others,
company-dependent fields turn out to be horrendously inefficient (more
or less entirely unbatched): near 80% of runtime is taken by setting
those fields on the partners being created, split 28%/51% between
getting the default values (_default_company_dependent) and setting
them (_inverse_company_dependent).

clear_cache on *every* set_multi call is a bit extreme and cause test
problems (test_pickings_transfer_with_different_uom_and_back_orders
has a super fucky behaviour wrt caching so breaks when clearing the
cache on every set_multi) so only cache the "default"
case (res_id=False) and only clear when specifically setting that.

This makes fetching default values essentially disappear from the
profile.
This commit is contained in:
Xavier Morel
2018-09-11 16:43:55 +02:00
committed by xmo-odoo
parent 7f29b31cf3
commit 0ecc5d50fb
+63 -10
View File
@@ -1,11 +1,9 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from operator import itemgetter
from odoo import api, fields, models, _
from odoo.exceptions import UserError
from odoo.tools import pycompat
from odoo.tools import pycompat, ormcache
TYPE2FIELD = {
'char': 'value_text',
@@ -87,12 +85,43 @@ class Property(models.Model):
@api.multi
def write(self, values):
return super(Property, self).write(self._update_values(values))
# if any of the records we're writing on has a res_id=False *or*
# we're writing a res_id=False on any record
default_set = False
if self._ids:
self.env.cr.execute(
'SELECT EXISTS (SELECT 1 FROM ir_property WHERE id in %s AND res_id IS NULL)', [self._ids])
default_set = self.env.cr.rowcount == 1 or any(
v.get('res_id') is False
for v in values
)
r = super(Property, self).write(self._update_values(values))
if default_set:
self.clear_caches()
return r
@api.model_create_multi
def create(self, vals_list):
vals_list = [self._update_values(vals) for vals in vals_list]
return super(Property, self).create(vals_list)
created_default = any(not v.get('res_id') for v in vals_list)
r = super(Property, self).create(vals_list)
if created_default:
self.clear_caches()
return r
@api.multi
def unlink(self):
default_deleted = False
if self._ids:
self.env.cr.execute(
'SELECT EXISTS (SELECT 1 FROM ir_property WHERE id in %s)',
[self._ids]
)
default_deleted = self.env.cr.rowcount == 1
r = super().unlink()
if default_deleted:
self.clear_caches()
return r
@api.multi
def get_by_record(self):
@@ -122,14 +151,39 @@ class Property(models.Model):
@api.model
def get(self, name, model, res_id=False):
if not res_id:
t, v = self._get_default_property(name, model)
if not v or t != 'many2one':
return v
return self.env[v[0]].browse(v[1])
p = self._get_property(name, model, res_id=res_id)
if p:
return p.get_by_record()
return False
# only cache Property.get(res_id=False) as that's
# sub-optimally, we can only call _company_default_get without a field
# unless we want to create a more complete helper which does the
# returning-a-company-id-from-a-model-and-name
COMPANY_KEY = "self.env.context.get('force_company') or self.env['res.company']._company_default_get(model).id"
@ormcache(COMPANY_KEY, 'name', 'model')
def _get_default_property(self, name, model):
prop = self._get_property(name, model, res_id=False)
if not prop:
return None, False
v = prop.get_by_record()
if prop.type != 'many2one':
return prop.type, v
return 'many2one', v and (v._name, v.id)
def _get_property(self, name, model, res_id):
domain = self._get_domain(name, model)
if domain is not None:
domain = [('res_id', '=', res_id)] + domain
#make the search with company_id asc to make sure that properties specific to a company are given first
prop = self.search(domain, limit=1, order='company_id')
if prop:
return prop.get_by_record()
return False
return self.search(domain, limit=1, order='company_id')
return self.browse(())
def _get_domain(self, prop_name, model):
self._cr.execute("SELECT id FROM ir_model_fields WHERE name=%s AND model=%s", (prop_name, model))
@@ -170,7 +224,6 @@ class Property(models.Model):
default_value = result.pop(False, False)
for id in ids:
result.setdefault(id, default_value)
return result
@api.model