[FIX] fields: fix `_column.new()` by relying on `to_field_args()`

The implementation was based on the ill-defined method `same_parameters()` that
compares arguments based on a heuristic.  Instead, we now create a new column
and check whether it is equivalent to `self` by comparing the arguments
returned by `to_field_args()`.  If that is the case, `self` is reused instead
of the new column.

The code refactoring also fixes the column reuse which was broken by the
introduction of the parameter `compute` in commit 9333c62.  Indeed, with that
parameter, `same_parameters()` always returned False, since old-api columns do
not have that parameter by default.  The parameter has been renamed to
`_computed_field`, and is no longer passed for creating columns.
This commit is contained in:
Raphael Collet 2015-02-23 11:29:03 +01:00
parent f23e47539e
commit 5ec8596f67
2 changed files with 16 additions and 20 deletions

View File

@ -658,7 +658,7 @@ class Field(object):
self.column = fields.property(**args)
elif self.column:
# let the column provide a valid column for the given parameters
self.column = self.column.new(**args)
self.column = self.column.new(_computed_field=bool(self.compute), **args)
else:
# create a fresh new column of the right type
self.column = getattr(fields, self.type)(**args)
@ -677,7 +677,6 @@ class Field(object):
_column_groups = property(attrgetter('groups'))
_column_change_default = property(attrgetter('change_default'))
_column_deprecated = property(attrgetter('deprecated'))
_column_compute = property(lambda self: bool(self.compute))
############################################################################
#

View File

@ -115,6 +115,7 @@ class _column(object):
"""
args0 = {
'string': string,
'help': args.pop('help', None),
'required': required,
'readonly': readonly,
'_domain': domain,
@ -127,6 +128,9 @@ class _column(object):
'translate': translate,
'select': select,
'manual': manual,
'group_operator': args.pop('group_operator', None),
'groups': args.pop('groups', None),
'deprecated': args.pop('deprecated', None),
}
for key, val in args0.iteritems():
if val:
@ -140,30 +144,23 @@ class _column(object):
if not self._classic_write or self.deprecated or self.manual:
self._prefetch = False
def new(self, **args):
""" return a column like `self` with the given parameters """
def new(self, _computed_field=False, **args):
""" Return a column like `self` with the given parameters; the parameter
`_computed_field` tells whether the corresponding field is computed.
"""
# memory optimization: reuse self whenever possible; you can reduce the
# average memory usage per registry by 10 megabytes!
return self if self.same_parameters(args) else type(self)(**args)
def same_parameters(self, args):
dummy = object()
return all(
# either both are falsy, or they are equal
(not val1 and not val) or (val1 == val)
for key, val in args.iteritems()
for val1 in [getattr(self, key, getattr(self, '_' + key, dummy))]
)
column = type(self)(**args)
return self if self.to_field_args() == column.to_field_args() else column
def to_field(self):
""" convert column `self` to a new-style field """
from openerp.fields import Field
return Field.by_type[self._type](**self.to_field_args())
return Field.by_type[self._type](column=self, **self.to_field_args())
def to_field_args(self):
""" return a dictionary with all the arguments to pass to the field """
base_items = [
('column', self), # field interfaces self
('copy', self.copy),
]
truthy_items = filter(itemgetter(1), [
@ -349,7 +346,7 @@ class float(_column):
# synopsis: digits_compute(cr) -> (precision, scale)
self.digits_compute = digits_compute
def new(self, **args):
def new(self, _computed_field=False, **args):
# float columns are database-dependent, so always recreate them
return type(self)(**args)
@ -1292,9 +1289,9 @@ class function(_column):
self._symbol_f = type_class._symbol_f
self._symbol_set = type_class._symbol_set
def new(self, **args):
if args.get('compute'):
# field is computed, we need an instance of the given type
def new(self, _computed_field=False, **args):
if _computed_field:
# field is computed, we need an instance of a non-function column
type_class = globals()[self._type]
return type_class(**args)
else: