[IMP] fields: turn field.digits and column.digits into dynamic properties
The computed value of parameter digits is no longer stored into fields and columns; instead the value is recomputed everytime it is needed. Note that performance is not an issue, since the method `get_precision` of model 'decimal.precision' is cached by the orm. This simplifies the management of digits on fields and saves about 300Kb per registry.
This commit is contained in:
parent
bf703fd9a3
commit
9aad3d873b
|
@ -48,11 +48,6 @@ class decimal_precision(orm.Model):
|
|||
def clear_cache(self, cr):
|
||||
"""clear cache and update models. Notify other workers to restart their registry."""
|
||||
self.precision_get.clear_cache(self)
|
||||
env = openerp.api.Environment(cr, SUPERUSER_ID, {})
|
||||
for model in self.pool.values():
|
||||
for field in model._fields.values():
|
||||
if field.type == 'float':
|
||||
field._setup_digits(env)
|
||||
RegistryManager.signal_registry_change(cr.dbname)
|
||||
|
||||
def create(self, cr, uid, data, context=None):
|
||||
|
|
|
@ -56,11 +56,15 @@ del time
|
|||
# The hard-coded super-user id (a.k.a. administrator, or root user).
|
||||
SUPERUSER_ID = 1
|
||||
|
||||
def registry(database_name):
|
||||
def registry(database_name=None):
|
||||
"""
|
||||
Return the model registry for the given database. If the registry does not
|
||||
exist yet, it is created on the fly.
|
||||
Return the model registry for the given database, or the database mentioned
|
||||
on the current thread. If the registry does not exist yet, it is created on
|
||||
the fly.
|
||||
"""
|
||||
if database_name is None:
|
||||
import threading
|
||||
database_name = threading.currentThread().dbname
|
||||
return modules.registry.RegistryManager.get(database_name)
|
||||
|
||||
#----------------------------------------------------------
|
||||
|
|
|
@ -991,26 +991,28 @@ class Float(Field):
|
|||
"""
|
||||
type = 'float'
|
||||
_digits = None # digits argument passed to class initializer
|
||||
digits = None # digits as computed by setup()
|
||||
group_operator = None # operator for aggregating values
|
||||
|
||||
def __init__(self, string=None, digits=None, **kwargs):
|
||||
super(Float, self).__init__(string=string, _digits=digits, **kwargs)
|
||||
|
||||
@property
|
||||
def digits(self):
|
||||
if callable(self._digits):
|
||||
with registry().cursor() as cr:
|
||||
return self._digits(cr)
|
||||
else:
|
||||
return self._digits
|
||||
|
||||
def _setup_digits(self, env):
|
||||
""" Setup the digits for `self` and its corresponding column """
|
||||
self.digits = self._digits(env.cr) if callable(self._digits) else self._digits
|
||||
if self.digits:
|
||||
assert isinstance(self.digits, (tuple, list)) and len(self.digits) >= 2, \
|
||||
"Float field %s with digits %r, expecting (total, decimal)" % (self, self.digits)
|
||||
if self.column:
|
||||
self.column.digits_change(env.cr)
|
||||
pass
|
||||
|
||||
def _setup_regular(self, env):
|
||||
super(Float, self)._setup_regular(env)
|
||||
self._setup_digits(env)
|
||||
|
||||
_related_digits = property(attrgetter('digits'))
|
||||
_related__digits = property(attrgetter('_digits'))
|
||||
_related_group_operator = property(attrgetter('group_operator'))
|
||||
|
||||
_description_digits = property(attrgetter('digits'))
|
||||
|
@ -1021,10 +1023,9 @@ class Float(Field):
|
|||
|
||||
def convert_to_cache(self, value, record, validate=True):
|
||||
# apply rounding here, otherwise value in cache may be wrong!
|
||||
if self.digits:
|
||||
return float_round(float(value or 0.0), precision_digits=self.digits[1])
|
||||
else:
|
||||
return float(value or 0.0)
|
||||
value = float(value or 0.0)
|
||||
digits = self.digits
|
||||
return float_round(value, precision_digits=digits[1]) if digits else value
|
||||
|
||||
|
||||
class _String(Field):
|
||||
|
@ -1790,7 +1791,7 @@ class Id(Field):
|
|||
raise TypeError("field 'id' cannot be assigned")
|
||||
|
||||
# imported here to avoid dependency cycle issues
|
||||
from openerp import SUPERUSER_ID
|
||||
from openerp import SUPERUSER_ID, registry
|
||||
from .exceptions import Warning, AccessError, MissingError
|
||||
from .models import BaseModel, MAGIC_COLUMNS
|
||||
from .osv import fields
|
||||
|
|
|
@ -659,8 +659,6 @@ class BaseModel(object):
|
|||
|
||||
# process store of low-level function fields
|
||||
for fname, column in cls._columns.iteritems():
|
||||
if hasattr(column, 'digits_change'):
|
||||
column.digits_change(cr)
|
||||
# filter out existing store about this field
|
||||
pool._store_function[cls._name] = [
|
||||
stored
|
||||
|
|
|
@ -51,7 +51,7 @@ from openerp.tools.translate import _
|
|||
from openerp.tools import float_round, float_repr
|
||||
from openerp.tools import html_sanitize
|
||||
import simplejson
|
||||
from openerp import SUPERUSER_ID
|
||||
from openerp import SUPERUSER_ID, registry
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -334,36 +334,42 @@ class html(text):
|
|||
|
||||
import __builtin__
|
||||
|
||||
def _symbol_set_float(self, x):
|
||||
result = __builtin__.float(x or 0.0)
|
||||
digits = self.digits
|
||||
if digits:
|
||||
precision, scale = digits
|
||||
result = float_repr(float_round(result, precision_digits=scale), precision_digits=scale)
|
||||
return result
|
||||
|
||||
class float(_column):
|
||||
_type = 'float'
|
||||
_symbol_c = '%s'
|
||||
_symbol_f = lambda x: __builtin__.float(x or 0.0)
|
||||
_symbol_set = (_symbol_c, _symbol_f)
|
||||
_symbol_get = lambda self,x: x or 0.0
|
||||
|
||||
@property
|
||||
def digits(self):
|
||||
if self._digits_compute:
|
||||
with registry().cursor() as cr:
|
||||
return self._digits_compute(cr)
|
||||
else:
|
||||
return self._digits
|
||||
|
||||
def __init__(self, string='unknown', digits=None, digits_compute=None, required=False, **args):
|
||||
_column.__init__(self, string=string, required=required, **args)
|
||||
self.digits = digits
|
||||
# synopsis: digits_compute(cr) -> (precision, scale)
|
||||
self.digits_compute = digits_compute
|
||||
|
||||
def new(self, _computed_field=False, **args):
|
||||
# float columns are database-dependent, so always recreate them
|
||||
return type(self)(**args)
|
||||
self._digits = digits
|
||||
self._digits_compute = digits_compute
|
||||
self._symbol_f = lambda x: _symbol_set_float(self, x)
|
||||
self._symbol_set = (self._symbol_c, self._symbol_f)
|
||||
|
||||
def to_field_args(self):
|
||||
args = super(float, self).to_field_args()
|
||||
args['digits'] = self.digits_compute or self.digits
|
||||
args['digits'] = self._digits_compute or self._digits
|
||||
return args
|
||||
|
||||
def digits_change(self, cr):
|
||||
if self.digits_compute:
|
||||
self.digits = self.digits_compute(cr)
|
||||
if self.digits:
|
||||
precision, scale = self.digits
|
||||
self._symbol_set = ('%s', lambda x: float_repr(float_round(__builtin__.float(x or 0.0),
|
||||
precision_digits=scale),
|
||||
precision_digits=scale))
|
||||
pass
|
||||
|
||||
class date(_column):
|
||||
_type = 'date'
|
||||
|
@ -1240,10 +1246,22 @@ class function(_column):
|
|||
# function fields are not copied by default
|
||||
copy = False
|
||||
|
||||
@property
|
||||
def digits(self):
|
||||
if self._digits_compute:
|
||||
with registry().cursor() as cr:
|
||||
return self._digits_compute(cr)
|
||||
else:
|
||||
return self._digits
|
||||
|
||||
#
|
||||
# multi: compute several fields in one call
|
||||
#
|
||||
def __init__(self, fnct, arg=None, fnct_inv=None, fnct_inv_arg=None, type='float', fnct_search=None, obj=None, store=False, multi=False, **args):
|
||||
# pop attributes that should not be assigned to self
|
||||
self._digits = args.pop('digits', (16,2))
|
||||
self._digits_compute = args.pop('digits_compute', None)
|
||||
|
||||
_column.__init__(self, **args)
|
||||
self._obj = obj
|
||||
self._fnct = fnct
|
||||
|
@ -1253,8 +1271,6 @@ class function(_column):
|
|||
if 'relation' in args:
|
||||
self._obj = args['relation']
|
||||
|
||||
self.digits = args.get('digits', (16,2))
|
||||
self.digits_compute = args.get('digits_compute', None)
|
||||
if callable(args.get('selection')):
|
||||
from openerp import api
|
||||
self.selection = api.expected(api.cr_uid_context, args['selection'])
|
||||
|
@ -1283,6 +1299,10 @@ class function(_column):
|
|||
self._symbol_c = char._symbol_c
|
||||
self._symbol_f = lambda x: _symbol_set_char(self, x)
|
||||
self._symbol_set = (self._symbol_c, self._symbol_f)
|
||||
elif type == 'float':
|
||||
self._symbol_c = float._symbol_c
|
||||
self._symbol_f = lambda x: _symbol_set_float(self, x)
|
||||
self._symbol_set = (self._symbol_c, self._symbol_f)
|
||||
else:
|
||||
type_class = globals().get(type)
|
||||
if type_class is not None:
|
||||
|
@ -1304,7 +1324,7 @@ class function(_column):
|
|||
args = super(function, self).to_field_args()
|
||||
args['store'] = bool(self.store)
|
||||
if self._type in ('float',):
|
||||
args['digits'] = self.digits_compute or self.digits
|
||||
args['digits'] = self._digits_compute or self._digits
|
||||
elif self._type in ('selection', 'reference'):
|
||||
args['selection'] = self.selection
|
||||
elif self._type in ('many2one', 'one2many', 'many2many'):
|
||||
|
@ -1312,14 +1332,7 @@ class function(_column):
|
|||
return args
|
||||
|
||||
def digits_change(self, cr):
|
||||
if self._type == 'float':
|
||||
if self.digits_compute:
|
||||
self.digits = self.digits_compute(cr)
|
||||
if self.digits:
|
||||
precision, scale = self.digits
|
||||
self._symbol_set = ('%s', lambda x: float_repr(float_round(__builtin__.float(x or 0.0),
|
||||
precision_digits=scale),
|
||||
precision_digits=scale))
|
||||
pass
|
||||
|
||||
def search(self, cr, uid, obj, name, args, context=None):
|
||||
if not self._fnct_search:
|
||||
|
|
Loading…
Reference in New Issue