summaryrefslogtreecommitdiff
path: root/odoo
diff options
context:
space:
mode:
authorXavier Morel <xmo@odoo.com>2018-08-21 15:20:04 +0200
committerxmo-odoo <xmo@odoo.com>2018-09-11 16:43:55 +0200
commit0ecc5d50fb6bc2a8a41a0653edff4e52846acc29 (patch)
tree247db07fd4f9607aeddee5c536e01d8dd1841aee /odoo
parent7f29b31cf307e640806225440be69561f9eea0b2 (diff)
[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.
Diffstat (limited to 'odoo')
-rw-r--r--odoo/addons/base/models/ir_property.py73
1 files changed, 63 insertions, 10 deletions
diff --git a/odoo/addons/base/models/ir_property.py b/odoo/addons/base/models/ir_property.py
index 8ec49e16ea7..ad3f6586926 100644
--- a/odoo/addons/base/models/ir_property.py
+++ b/odoo/addons/base/models/ir_property.py
@@ -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