Rationale ========= Since v8, the `display_name` field is present on all models. By default, `display_name` uses `name_get` which has pretty much the same purpose (return record name used by the web client). Gradually, many (backend) developers (and the ORM: https://github.com/odoo/odoo/commit/6da1c3ac4c036eac289597602976538e243cb939) started using `display_name` (more convenient than `record.name_get()[0][1]`) but it still had the `name_get` override. It becomes more complex than necessary and poeple start to misunderstand the two (and sometimes override both, leading to inconstiencies between `display_name`/`name_get`). To simplify the ORM and the API, we decided to keep only one of them, the `display_name` field: - It is much more convenient from a backend point of view (`record.name_get()[0][1]` vs `record.display_name`) - It is cached during the same transaction (and invalidated if its dependencies change) - It can be overridden like any other compute field (override `_compute_display_name` with any extra dependencies) - `name_get` is replaced by `read(['display_name'])` (API perceptive), which can actually be more efficient (if `display_name`'s depends are correct, the ORM will only fetch the fields it needs instead of every prefetchable field) Changes ======= - Deprecates `name_get` for the v17 and based the method on `display_name` (the opposite of before) - Converts all usage of `name_get` - Overrides of `name_get` are now overrides of `_compute_display_name` - For `res.partner`, rename the field store `display_name` into `complete_name` because `display_name` context-dependent and it makes no sense to have a compute store that is context-dependent. - Previously, it was possible to return multiple names for the same record with `name_get`, but it was tricky and most of the usage of this `name_get` didn't take this into account. The only example of this is the `name_get` of `product.product` (now use `", ".join(<names>)`). Part-of: odoo/odoo#122085
96 lines
2.8 KiB
Python
96 lines
2.8 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
import json
|
|
|
|
from odoo import fields
|
|
|
|
|
|
def monkey_patch(cls):
|
|
""" Return a method decorator to monkey-patch the given class. """
|
|
def decorate(func):
|
|
name = func.__name__
|
|
func.super = getattr(cls, name, None)
|
|
setattr(cls, name, func)
|
|
return func
|
|
return decorate
|
|
|
|
|
|
#
|
|
# Implement sparse fields by monkey-patching fields.Field
|
|
#
|
|
|
|
fields.Field.__doc__ += """
|
|
|
|
.. _field-sparse:
|
|
|
|
.. rubric:: Sparse fields
|
|
|
|
Sparse fields have a very small probability of being not null. Therefore
|
|
many such fields can be serialized compactly into a common location, the
|
|
latter being a so-called "serialized" field.
|
|
|
|
:param sparse: the name of the field where the value of this field must
|
|
be stored.
|
|
"""
|
|
fields.Field.sparse = None
|
|
|
|
@monkey_patch(fields.Field)
|
|
def _get_attrs(self, model_class, name):
|
|
attrs = _get_attrs.super(self, model_class, name)
|
|
if attrs.get('sparse'):
|
|
# by default, sparse fields are not stored and not copied
|
|
attrs['store'] = False
|
|
attrs['copy'] = attrs.get('copy', False)
|
|
attrs['compute'] = self._compute_sparse
|
|
if not attrs.get('readonly'):
|
|
attrs['inverse'] = self._inverse_sparse
|
|
return attrs
|
|
|
|
@monkey_patch(fields.Field)
|
|
def _compute_sparse(self, records):
|
|
for record in records:
|
|
values = record[self.sparse]
|
|
record[self.name] = values.get(self.name)
|
|
if self.relational:
|
|
for record in records:
|
|
record[self.name] = record[self.name].exists()
|
|
|
|
@monkey_patch(fields.Field)
|
|
def _inverse_sparse(self, records):
|
|
for record in records:
|
|
values = record[self.sparse]
|
|
value = self.convert_to_read(record[self.name], record, use_display_name=False)
|
|
if value:
|
|
if values.get(self.name) != value:
|
|
values[self.name] = value
|
|
record[self.sparse] = values
|
|
else:
|
|
if self.name in values:
|
|
values.pop(self.name)
|
|
record[self.sparse] = values
|
|
|
|
|
|
#
|
|
# Definition and implementation of serialized fields
|
|
#
|
|
|
|
class Serialized(fields.Field):
|
|
""" Serialized fields provide the storage for sparse fields. """
|
|
type = 'serialized'
|
|
column_type = ('text', 'text')
|
|
|
|
prefetch = False # not prefetched by default
|
|
|
|
def convert_to_column(self, value, record, values=None, validate=True):
|
|
return self.convert_to_cache(value, record, validate=validate)
|
|
|
|
def convert_to_cache(self, value, record, validate=True):
|
|
# cache format: json.dumps(value) or None
|
|
return json.dumps(value) if isinstance(value, dict) else (value or None)
|
|
|
|
def convert_to_record(self, value, record):
|
|
return json.loads(value or "{}")
|
|
|
|
|
|
fields.Serialized = Serialized
|