diff options
| author | Raphael Collet <rco@odoo.com> | 2019-09-11 08:21:33 +0000 |
|---|---|---|
| committer | Raphael Collet <rco@odoo.com> | 2019-09-12 14:24:58 +0000 |
| commit | 492e4cc5a12fb062e106836fc35889072b85453c (patch) | |
| tree | 93eb8da311aed058a79ce7bc2456f645662dff71 | |
| parent | 956338f28af96dc53bb15ac1c0a7d9888f314b18 (diff) | |
[REF] fields: setup and use of `depends_context`
closes odoo/odoo#36795
Signed-off-by: Raphael Collet (rco) <rco@openerp.com>
| -rw-r--r-- | odoo/api.py | 56 | ||||
| -rw-r--r-- | odoo/fields.py | 56 |
2 files changed, 60 insertions, 52 deletions
diff --git a/odoo/api.py b/odoo/api.py index 65ae87f2b92..4015156f1a3 100644 --- a/odoo/api.py +++ b/odoo/api.py @@ -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) @@ -693,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, ()) @@ -720,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: @@ -731,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 @@ -739,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 @@ -756,10 +746,10 @@ 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] @@ -769,11 +759,11 @@ class Cache(object): 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 = 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 ids = [] for record_id in records._ids: try: - if key: + if key is not None: val = field_cache[record_id][key] else: val = field_cache[record_id] @@ -787,10 +777,14 @@ class Cache(object): 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``. """ diff --git a/odoo/fields.py b/odoo/fields.py index da283c226d3..dc685d7885b 100644 --- a/odoo/fields.py +++ b/odoo/fields.py @@ -498,30 +498,24 @@ class Field(MetaField('DummyField', (object,), {})): if self.depends is not None: return - def get_depends(func): - deps = getattr(func, '_depends', ()) - return deps(model) if callable(deps) else deps - - def get_depends_context(func): - return getattr(func, '_depends_context', ()) - + # determine the functions implementing self.compute if isinstance(self.compute, str): - # if the compute method has been overridden, concatenate all their _depends - self.depends = tuple( - dep - for method in resolve_mro(model, self.compute, callable) - for dep in get_depends(method) - ) - depends_context = tuple( - dep - for method in resolve_mro(model, self.compute, callable) - for dep in get_depends_context(method) - ) + funcs = resolve_mro(model, self.compute, callable) + elif self.compute: + funcs = [self.compute] else: - self.depends = tuple(get_depends(self.compute)) - depends_context = tuple(get_depends_context(self.compute)) + funcs = [] + + # collect depends and depends_context + depends = [] + depends_context = list(self.depends_context or ()) + for func in funcs: + deps = getattr(func, '_depends', ()) + depends.extend(deps(model) if callable(deps) else deps) + depends_context.extend(getattr(func, '_depends_context', ())) - self.depends_context = (self.depends_context or ()) + depends_context + self.depends = tuple(depends) + self.depends_context = tuple(depends_context) # display_name may depend on context['lang'] (`test_lp1071710`) if self.automatic and self.name == 'display_name' and model._rec_name: @@ -699,6 +693,26 @@ class Field(MetaField('DummyField', (object,), {})): return Property.search_multi(self.name, self.model_name, operator, value) # + # Cache key for context-dependent fields + # + + def cache_key(self, env): + """ Return the cache key corresponding to ``self.depends_context``. """ + 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', self.context.get('active_test', True)) + else: + return get_context(key) + + return tuple(get(key) for key in self.depends_context) + + # # Setup of field triggers # |
