[REF] models, fields: refactor fields_get

- Filter in attributes rather than filter out.
  The goal is to not gather attributes which are
  costly in term of performance if they are not requested
  in the first place.
  e.g., with all modules installed:
  - Before rev:
    ```py
       In [1]: %time for _i in range(1000): self.env["res.partner"].fields_get(attributes=['readonly', 'required', 'states', 'invisible']);self.invalidate_cache()
        CPU times: user 1.99 s, sys: 9.83 ms, total: 2 s
        Wall time: 2.03 s
    ```
  - After rev:
    ```py
        In [2]: %time for _i in range(1000): self.env["res.partner"].fields_get(attributes=['readonly', 'required', 'states', 'invisible']);self.invalidate_cache()
        CPU times: user 345 ms, sys: 0 ns, total: 345 ms
        Wall time: 345 ms
    ```

- Use the `_description_` mechanism for the attributes `name` and `type`,
  so its no longer needed to treat them as exception in
  `field_get` and `get_description` respectively,
  and make the code shorter and cleaner.

- Move out from `fields_get` the block
  ```py
      has_access = functools.partial(self.check_access_rights, raise_exception=False)
      readonly = not (has_access('write') or has_access('create'))
      ...
      if readonly:
         description['readonly'] = True
         description['states'] = {}
  ```
  because:
  - It meant you had a different behavior using `fields_get` or `get_description`
    for the keys `readonly` and `states`, meaning a field could be marked as `readonly`
    by `fields_get` but not by `get_description`, which is confusing.
  - It is actually used in only one place, the post-process of back-end views,
    which mark the fields readonly for the web client if you do not have
    the create or write access to their model.

closes odoo/odoo#87273

Signed-off-by: Denis Ledoux (dle) <dle@odoo.com>
This commit is contained in:
Denis Ledoux
2022-04-29 13:12:49 +02:00
parent ed5f7007ff
commit b9feebc25c
3 changed files with 15 additions and 15 deletions
+8 -1
View File
@@ -3,6 +3,7 @@
import ast
import collections
import datetime
import functools
import inspect
import json
import logging
@@ -2195,7 +2196,13 @@ class NameManager:
@lazy_property
def field_info(self):
return self.model.fields_get()
field_info = self.model.fields_get()
has_access = functools.partial(self.model.check_access_rights, raise_exception=False)
if not (has_access('write') or has_access('create')):
for info in field_info.vals():
info['readonly'] = True
info['states'] = {}
return field_info
def has_field(self, name, info=frozendict()):
self.available_fields[name].update(info)
+6 -2
View File
@@ -793,10 +793,12 @@ class Field(MetaField('DummyField', (object,), {})):
# Field description
#
def get_description(self, env):
def get_description(self, env, attributes=None):
""" Return a dictionary that describes the field ``self``. """
desc = {'type': self.type}
desc = {}
for attr, prop in self.description_attrs:
if attributes is not None and attr not in attributes:
continue
value = getattr(self, prop)
if callable(value):
value = value(env)
@@ -806,6 +808,8 @@ class Field(MetaField('DummyField', (object,), {})):
return desc
# properties used by get_description()
_description_name = property(attrgetter('name'))
_description_type = property(attrgetter('type'))
_description_store = property(attrgetter('store'))
_description_manual = property(attrgetter('manual'))
_description_related = property(attrgetter('related'))
+1 -12
View File
@@ -3195,9 +3195,6 @@ class BaseModel(metaclass=MetaModel):
:param allfields: list of fields to document, all if empty or not provided
:param attributes: list of description attributes to return for each field, all if empty or not provided
"""
has_access = functools.partial(self.check_access_rights, raise_exception=False)
readonly = not (has_access('write') or has_access('create'))
res = {}
for fname, field in self._fields.items():
if allfields and fname not in allfields:
@@ -3205,15 +3202,7 @@ class BaseModel(metaclass=MetaModel):
if field.groups and not self.env.su and not self.user_has_groups(field.groups):
continue
description = field.get_description(self.env)
description['name'] = fname
if readonly:
description['readonly'] = True
description['states'] = {}
if attributes:
description = {key: val
for key, val in description.items()
if key in attributes}
description = field.get_description(self.env, attributes=attributes)
res[fname] = description
return res