summaryrefslogtreecommitdiff
path: root/odoo/api.py
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2019-09-13 18:07:59 +0200
committerChristophe Simonis <chs@odoo.com>2019-09-13 18:07:59 +0200
commit6c948c36bbf72dd896e0bff03aa7fbddadcbb0cd (patch)
treef8620905c011735d8a3c340c46d1417a54a75972 /odoo/api.py
parent48503fcf08eac6fed12e080d4f501f9bbc8d257d (diff)
parent197474e5b9ad43ab2d587a98136a1a809ce9a2e7 (diff)
[MERGE] forward port branch saas-12.5 up to 197474e5b9a
Diffstat (limited to 'odoo/api.py')
-rw-r--r--odoo/api.py99
1 files changed, 56 insertions, 43 deletions
diff --git a/odoo/api.py b/odoo/api.py
index 41439486d35..4015156f1a3 100644
--- a/odoo/api.py
+++ b/odoo/api.py
@@ -51,7 +51,7 @@ from decorator import decorate, decorator
from werkzeug.local import Local, release_local
import odoo
-from odoo.tools import frozendict, classproperty, StackMap
+from odoo.tools import frozendict, classproperty, lazy_property, StackMap
from odoo.exceptions import CacheMiss
_logger = logging.getLogger(__name__)
@@ -213,9 +213,9 @@ def depends(*args):
def depends_context(*args):
- """ Return a decorator that specifies the context dependencies of a non-stored "compute"
- method (for new-style function fields). Each argument must be a string
- that consists in a key in the context::
+ """ Return a decorator that specifies the context dependencies of a
+ non-stored "compute" method. Each argument is a key in the context's
+ dictionary::
price = fields.Float(compute='_compute_product_price')
@@ -227,6 +227,12 @@ def depends_context(*args):
else:
pricelist = self.env['product.pricelist'].get_default_pricelist()
product.price = pricelist.get_products_price(product).get(product.id, 0.0)
+
+ All dependencies must be hashable. The following keys have special
+ support:
+ - 'force_company' (value in context or current company id),
+ - 'uid' (current user id and superuser flag),
+ - 'active_test' (value in env.context or value in field.context).
"""
return attrsetter('_depends_context', args)
@@ -518,23 +524,22 @@ class Environment(Mapping):
superuser mode. """
return self.su or self.user._is_system()
- @property
+ @lazy_property
def user(self):
""" return the current user (as an instance) """
return self(su=True)['res.users'].browse(self.uid)
- @property
+ @lazy_property
def company(self):
""" return the company in which the user is logged in (as an instance) """
- try:
- company_id = int(self.context.get('allowed_company_ids')[0])
+ company_ids = self.context.get('allowed_company_ids', False)
+ if company_ids:
+ company_id = int(company_ids[0])
if company_id in self.user.company_ids.ids:
return self['res.company'].browse(company_id)
- return self.user.company_id
- except Exception:
- return self.user.company_id
+ return self.user.company_id
- @property
+ @lazy_property
def companies(self):
""" return a recordset of the enabled companies by the user """
try: # In case the user tries to bidouille the url (eg: cids=1,foo,bar)
@@ -637,14 +642,16 @@ class Environment(Mapping):
""" Return whether ``field`` must be computed on ``record``. """
return record.id in self.all.tocompute.get(field, ())
+ def not_to_compute(self, field, records):
+ """ Return the subset of ``records`` for which ``field`` must not be computed. """
+ ids = self.all.tocompute.get(field, ())
+ return records.browse(id_ for id_ in records._ids if id_ not in ids)
+
def add_to_compute(self, field, records):
- """ Mark ``field`` to be computed on ``records``, return newly added records. """
+ """ Mark ``field`` to be computed on ``records``. """
if not records:
return records
- ids = self.all.tocompute[field]
- added_ids = [id_ for id_ in records._ids if id_ not in ids]
- ids.update(added_ids)
- return records.browse(added_ids)
+ self.all.tocompute[field].update(records._ids)
def remove_to_compute(self, field, records):
""" Mark ``field`` as computed on ``records``. """
@@ -692,25 +699,10 @@ class Cache(object):
# {field: {record_id: value}}
self._data = defaultdict(dict)
- def _get_context_key(self, env, field):
- get_context = env.context.get
-
- def get(key):
- if key == 'force_company':
- return get_context('force_company') or env.company.id
- elif key == 'uid':
- return (env.uid, env.su)
- elif key == 'active_test':
- return get_context('active_test', field.context.get('active_test', True))
- else:
- return get_context(key)
-
- return tuple(get(key) for key in field.depends_context)
-
def contains(self, record, field):
""" Return whether ``record`` has a value for ``field``. """
if field.depends_context:
- key = self._get_context_key(record.env, field)
+ key = field.cache_key(record.env)
return key in self._data.get(field, {}).get(record.id, {})
return record.id in self._data.get(field, ())
@@ -719,8 +711,7 @@ class Cache(object):
try:
value = self._data[field][record._ids[0]]
if field.depends_context:
- key = self._get_context_key(record.env, field)
- value = value[key]
+ value = value[field.cache_key(record.env)]
return value
except KeyError:
if default is NOTHING:
@@ -730,7 +721,7 @@ class Cache(object):
def set(self, record, field, value):
""" Set the value of ``field`` for ``record``. """
if field.depends_context:
- key = self._get_context_key(record.env, field)
+ key = field.cache_key(record.env)
self._data[field].setdefault(record._ids[0], {})[key] = value
else:
self._data[field][record._ids[0]] = value
@@ -738,7 +729,7 @@ class Cache(object):
def update(self, records, field, values):
""" Set the values of ``field`` for several ``records``. """
if field.depends_context:
- key = self._get_context_key(records.env, field)
+ key = field.cache_key(records.env)
field_cache = self._data[field]
for record_id, value in zip(records._ids, values):
field_cache.setdefault(record_id, {})[key] = value
@@ -755,23 +746,45 @@ class Cache(object):
def get_values(self, records, field):
""" Return the cached values of ``field`` for ``records``. """
field_cache = self._data[field]
- key = self._get_context_key(records.env, field) if field.depends_context else None
+ key = field.cache_key(records.env) if field.depends_context else None
for record_id in records._ids:
try:
- if key:
+ if key is not None:
yield field_cache[record_id][key]
else:
yield field_cache[record_id]
except KeyError:
pass
+ def get_records_different_from(self, records, field, value):
+ """ Return the subset of ``records`` that has not ``value`` for ``field``. """
+ field_cache = self._data[field]
+ key = field.cache_key(records.env) if field.depends_context else None
+ ids = []
+ for record_id in records._ids:
+ try:
+ if key is not None:
+ val = field_cache[record_id][key]
+ else:
+ val = field_cache[record_id]
+ except KeyError:
+ ids.append(record_id)
+ else:
+ if val != value:
+ ids.append(record_id)
+ return records.browse(ids)
+
def get_fields(self, record):
""" Return the fields with a value for ``record``. """
for name, field in record._fields.items():
- values = self._data.get(field, ())
- key = self._get_context_key(record.env, field) if field.depends_context else None
- if name != 'id' and record.id in values and (not key or key in values[record.id]):
- yield field
+ if name == 'id':
+ continue
+ values = self._data.get(field, {})
+ if record.id not in values:
+ continue
+ if field.depends_context and field.cache_key(record.env) not in values[record.id]:
+ continue
+ yield field
def get_records(self, model, field):
""" Return the records of ``model`` that have a value for ``field``. """