[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):
|
def clear_cache(self, cr):
|
||||||
"""clear cache and update models. Notify other workers to restart their registry."""
|
"""clear cache and update models. Notify other workers to restart their registry."""
|
||||||
self.precision_get.clear_cache(self)
|
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)
|
RegistryManager.signal_registry_change(cr.dbname)
|
||||||
|
|
||||||
def create(self, cr, uid, data, context=None):
|
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).
|
# The hard-coded super-user id (a.k.a. administrator, or root user).
|
||||||
SUPERUSER_ID = 1
|
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
|
Return the model registry for the given database, or the database mentioned
|
||||||
exist yet, it is created on the fly.
|
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)
|
return modules.registry.RegistryManager.get(database_name)
|
||||||
|
|
||||||
#----------------------------------------------------------
|
#----------------------------------------------------------
|
||||||
|
|
|
@ -991,26 +991,28 @@ class Float(Field):
|
||||||
"""
|
"""
|
||||||
type = 'float'
|
type = 'float'
|
||||||
_digits = None # digits argument passed to class initializer
|
_digits = None # digits argument passed to class initializer
|
||||||
digits = None # digits as computed by setup()
|
|
||||||
group_operator = None # operator for aggregating values
|
group_operator = None # operator for aggregating values
|
||||||
|
|
||||||
def __init__(self, string=None, digits=None, **kwargs):
|
def __init__(self, string=None, digits=None, **kwargs):
|
||||||
super(Float, self).__init__(string=string, _digits=digits, **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):
|
def _setup_digits(self, env):
|
||||||
""" Setup the digits for `self` and its corresponding column """
|
""" Setup the digits for `self` and its corresponding column """
|
||||||
self.digits = self._digits(env.cr) if callable(self._digits) else self._digits
|
pass
|
||||||
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)
|
|
||||||
|
|
||||||
def _setup_regular(self, env):
|
def _setup_regular(self, env):
|
||||||
super(Float, self)._setup_regular(env)
|
super(Float, self)._setup_regular(env)
|
||||||
self._setup_digits(env)
|
self._setup_digits(env)
|
||||||
|
|
||||||
_related_digits = property(attrgetter('digits'))
|
_related__digits = property(attrgetter('_digits'))
|
||||||
_related_group_operator = property(attrgetter('group_operator'))
|
_related_group_operator = property(attrgetter('group_operator'))
|
||||||
|
|
||||||
_description_digits = property(attrgetter('digits'))
|
_description_digits = property(attrgetter('digits'))
|
||||||
|
@ -1021,10 +1023,9 @@ class Float(Field):
|
||||||
|
|
||||||
def convert_to_cache(self, value, record, validate=True):
|
def convert_to_cache(self, value, record, validate=True):
|
||||||
# apply rounding here, otherwise value in cache may be wrong!
|
# apply rounding here, otherwise value in cache may be wrong!
|
||||||
if self.digits:
|
value = float(value or 0.0)
|
||||||
return float_round(float(value or 0.0), precision_digits=self.digits[1])
|
digits = self.digits
|
||||||
else:
|
return float_round(value, precision_digits=digits[1]) if digits else value
|
||||||
return float(value or 0.0)
|
|
||||||
|
|
||||||
|
|
||||||
class _String(Field):
|
class _String(Field):
|
||||||
|
@ -1790,7 +1791,7 @@ class Id(Field):
|
||||||
raise TypeError("field 'id' cannot be assigned")
|
raise TypeError("field 'id' cannot be assigned")
|
||||||
|
|
||||||
# imported here to avoid dependency cycle issues
|
# 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 .exceptions import Warning, AccessError, MissingError
|
||||||
from .models import BaseModel, MAGIC_COLUMNS
|
from .models import BaseModel, MAGIC_COLUMNS
|
||||||
from .osv import fields
|
from .osv import fields
|
||||||
|
|
|
@ -659,8 +659,6 @@ class BaseModel(object):
|
||||||
|
|
||||||
# process store of low-level function fields
|
# process store of low-level function fields
|
||||||
for fname, column in cls._columns.iteritems():
|
for fname, column in cls._columns.iteritems():
|
||||||
if hasattr(column, 'digits_change'):
|
|
||||||
column.digits_change(cr)
|
|
||||||
# filter out existing store about this field
|
# filter out existing store about this field
|
||||||
pool._store_function[cls._name] = [
|
pool._store_function[cls._name] = [
|
||||||
stored
|
stored
|
||||||
|
|
|
@ -51,7 +51,7 @@ from openerp.tools.translate import _
|
||||||
from openerp.tools import float_round, float_repr
|
from openerp.tools import float_round, float_repr
|
||||||
from openerp.tools import html_sanitize
|
from openerp.tools import html_sanitize
|
||||||
import simplejson
|
import simplejson
|
||||||
from openerp import SUPERUSER_ID
|
from openerp import SUPERUSER_ID, registry
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -334,36 +334,42 @@ class html(text):
|
||||||
|
|
||||||
import __builtin__
|
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):
|
class float(_column):
|
||||||
_type = 'float'
|
_type = 'float'
|
||||||
_symbol_c = '%s'
|
_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
|
_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):
|
def __init__(self, string='unknown', digits=None, digits_compute=None, required=False, **args):
|
||||||
_column.__init__(self, string=string, required=required, **args)
|
_column.__init__(self, string=string, required=required, **args)
|
||||||
self.digits = digits
|
|
||||||
# synopsis: digits_compute(cr) -> (precision, scale)
|
# synopsis: digits_compute(cr) -> (precision, scale)
|
||||||
self.digits_compute = digits_compute
|
self._digits = digits
|
||||||
|
self._digits_compute = digits_compute
|
||||||
def new(self, _computed_field=False, **args):
|
self._symbol_f = lambda x: _symbol_set_float(self, x)
|
||||||
# float columns are database-dependent, so always recreate them
|
self._symbol_set = (self._symbol_c, self._symbol_f)
|
||||||
return type(self)(**args)
|
|
||||||
|
|
||||||
def to_field_args(self):
|
def to_field_args(self):
|
||||||
args = super(float, self).to_field_args()
|
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
|
return args
|
||||||
|
|
||||||
def digits_change(self, cr):
|
def digits_change(self, cr):
|
||||||
if self.digits_compute:
|
pass
|
||||||
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))
|
|
||||||
|
|
||||||
class date(_column):
|
class date(_column):
|
||||||
_type = 'date'
|
_type = 'date'
|
||||||
|
@ -1240,10 +1246,22 @@ class function(_column):
|
||||||
# function fields are not copied by default
|
# function fields are not copied by default
|
||||||
copy = False
|
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
|
# 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):
|
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)
|
_column.__init__(self, **args)
|
||||||
self._obj = obj
|
self._obj = obj
|
||||||
self._fnct = fnct
|
self._fnct = fnct
|
||||||
|
@ -1253,8 +1271,6 @@ class function(_column):
|
||||||
if 'relation' in args:
|
if 'relation' in args:
|
||||||
self._obj = args['relation']
|
self._obj = args['relation']
|
||||||
|
|
||||||
self.digits = args.get('digits', (16,2))
|
|
||||||
self.digits_compute = args.get('digits_compute', None)
|
|
||||||
if callable(args.get('selection')):
|
if callable(args.get('selection')):
|
||||||
from openerp import api
|
from openerp import api
|
||||||
self.selection = api.expected(api.cr_uid_context, args['selection'])
|
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_c = char._symbol_c
|
||||||
self._symbol_f = lambda x: _symbol_set_char(self, x)
|
self._symbol_f = lambda x: _symbol_set_char(self, x)
|
||||||
self._symbol_set = (self._symbol_c, self._symbol_f)
|
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:
|
else:
|
||||||
type_class = globals().get(type)
|
type_class = globals().get(type)
|
||||||
if type_class is not None:
|
if type_class is not None:
|
||||||
|
@ -1304,7 +1324,7 @@ class function(_column):
|
||||||
args = super(function, self).to_field_args()
|
args = super(function, self).to_field_args()
|
||||||
args['store'] = bool(self.store)
|
args['store'] = bool(self.store)
|
||||||
if self._type in ('float',):
|
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'):
|
elif self._type in ('selection', 'reference'):
|
||||||
args['selection'] = self.selection
|
args['selection'] = self.selection
|
||||||
elif self._type in ('many2one', 'one2many', 'many2many'):
|
elif self._type in ('many2one', 'one2many', 'many2many'):
|
||||||
|
@ -1312,14 +1332,7 @@ class function(_column):
|
||||||
return args
|
return args
|
||||||
|
|
||||||
def digits_change(self, cr):
|
def digits_change(self, cr):
|
||||||
if self._type == 'float':
|
pass
|
||||||
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))
|
|
||||||
|
|
||||||
def search(self, cr, uid, obj, name, args, context=None):
|
def search(self, cr, uid, obj, name, args, context=None):
|
||||||
if not self._fnct_search:
|
if not self._fnct_search:
|
||||||
|
|
Loading…
Reference in New Issue