Add digits mixin
issue10596 review338511002
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
* Add digits mixin
|
||||
* Use argon2 or scrypt to hash password by default
|
||||
* Allow PYSON Expression as key for PYSON In with dict object
|
||||
* Add method to format warning name with records
|
||||
|
||||
@@ -383,6 +383,8 @@ instance.
|
||||
Integers can be replaced by a :class:`~trytond.pyson.PYSON` statement.
|
||||
If digits is None or any values of the tuple is ``None``, no validation on
|
||||
the numbers will be done.
|
||||
The tuple can be replaced by a string containing the name of a
|
||||
:class:`Many2One` pointing to a :class:`DigitsMixin`.
|
||||
|
||||
Numeric
|
||||
-------
|
||||
|
||||
@@ -802,6 +802,21 @@ Instance methods:
|
||||
(1) the value. If no symbol parameter is supplied then the mixin uses the
|
||||
value of attribute named ``symbol``.
|
||||
|
||||
===========
|
||||
DigitsMixin
|
||||
===========
|
||||
|
||||
.. class:: DigitsMixin
|
||||
|
||||
A mixin_ to manage the digits of :attr:`Float.digits` and
|
||||
:attr:`Numeric.digits` from a :class:`Model`.
|
||||
|
||||
Instance methods:
|
||||
|
||||
.. method:: DigitsMixin.get_digits()
|
||||
|
||||
Return a tuple of two integers to use a ``digits`` attribute.
|
||||
|
||||
================
|
||||
sequence_ordered
|
||||
================
|
||||
|
||||
@@ -10,6 +10,7 @@ from .dictschema import DictSchemaMixin
|
||||
from .match import MatchMixin
|
||||
from .union import UnionMixin
|
||||
from .symbol import SymbolMixin
|
||||
from .digits import DigitsMixin
|
||||
from .multivalue import MultiValueMixin, ValueMixin
|
||||
from .descriptors import dualmethod
|
||||
from .order import sequence_ordered
|
||||
@@ -20,6 +21,6 @@ from .avatar import avatar_mixin
|
||||
__all__ = ['Model', 'ModelView', 'ModelStorage', 'ModelSingleton', 'ModelSQL',
|
||||
'Check', 'Unique', 'Exclude',
|
||||
'Workflow', 'DictSchemaMixin', 'MatchMixin', 'UnionMixin', 'dualmethod',
|
||||
'MultiValueMixin', 'ValueMixin', 'SymbolMixin',
|
||||
'MultiValueMixin', 'ValueMixin', 'SymbolMixin', 'DigitsMixin',
|
||||
'EvalEnvironment', 'sequence_ordered', 'DeactivableMixin', 'tree',
|
||||
'avatar_mixin']
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
from trytond.cache import Cache
|
||||
from trytond.rpc import RPC
|
||||
|
||||
|
||||
class DigitsMixin:
|
||||
__slots__ = ()
|
||||
|
||||
_digits_cache = Cache('_digits_mixin..get_digits', context=False)
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls.__rpc__.update({
|
||||
'get_digits': RPC(instantiate=0, cache=dict(days=1)),
|
||||
})
|
||||
|
||||
def get_digits(self):
|
||||
key = str(self)
|
||||
digits = self._digits_cache.get(key)
|
||||
if digits is not None:
|
||||
return digits
|
||||
digits = self._get_digits()
|
||||
self._digits_cache.set(key, digits)
|
||||
return digits
|
||||
|
||||
def _get_digits(self):
|
||||
return (16, self.digits)
|
||||
|
||||
@classmethod
|
||||
def write(cls, *args):
|
||||
super().write(*args)
|
||||
cls._digits_cache.clear()
|
||||
@@ -6,13 +6,15 @@ from .field import Field
|
||||
|
||||
def digits_validate(value):
|
||||
if value:
|
||||
assert isinstance(value, tuple), 'digits must be a tuple'
|
||||
for i in value:
|
||||
assert isinstance(i, (int, PYSON)), \
|
||||
'digits must be tuple of integers or PYSON'
|
||||
if isinstance(i, PYSON):
|
||||
assert i.types().issubset(set([int, int])), \
|
||||
'PYSON digits must return an integer'
|
||||
assert isinstance(value, (tuple, str)), \
|
||||
'digits must be a tuple or a string'
|
||||
if isinstance(value, tuple):
|
||||
for i in value:
|
||||
assert isinstance(i, (int, PYSON)), \
|
||||
'digits must be tuple of integers or PYSON'
|
||||
if isinstance(i, PYSON):
|
||||
assert i.types().issubset(set([int, int])), \
|
||||
'PYSON digits must return an integer'
|
||||
|
||||
|
||||
class Float(Field):
|
||||
@@ -31,6 +33,12 @@ class Float(Field):
|
||||
:param digits: a list of two integers defining the total
|
||||
of digits and the number of decimals of the float.
|
||||
'''
|
||||
if isinstance(digits, str):
|
||||
if depends is None:
|
||||
depends = [digits]
|
||||
elif digits not in depends:
|
||||
depends = depends.copy()
|
||||
depends.append(digits)
|
||||
super(Float, self).__init__(string=string, help=help,
|
||||
required=required, readonly=readonly, domain=domain, states=states,
|
||||
select=select, on_change=on_change, on_change_with=on_change_with,
|
||||
|
||||
@@ -1268,7 +1268,7 @@ class ModelStorage(Model):
|
||||
gettext('ir.msg_size_validation_record',
|
||||
**error_args))
|
||||
|
||||
def digits_test(value, digits, field_name):
|
||||
def digits_test(record, digits, field_name):
|
||||
def raise_error(value):
|
||||
error_args = cls.__names__(field_name)
|
||||
error_args['digits'] = digits[1]
|
||||
@@ -1276,6 +1276,14 @@ class ModelStorage(Model):
|
||||
raise DigitsValidationError(
|
||||
gettext('ir.msg_digits_validation_record',
|
||||
**error_args))
|
||||
|
||||
value = getattr(record, field_name)
|
||||
if isinstance(digits, str):
|
||||
digit_record = getattr(record, digits)
|
||||
if digit_record:
|
||||
digits = digit_record.get_digits()
|
||||
else:
|
||||
digits = None
|
||||
if (value is None
|
||||
or not digits
|
||||
or any(d is None for d in digits)):
|
||||
@@ -1294,12 +1302,10 @@ class ModelStorage(Model):
|
||||
for record in records:
|
||||
digits = _record_eval_pyson(
|
||||
record, pyson_digits, encoded=True)
|
||||
digits_test(getattr(record, field_name), digits,
|
||||
field_name)
|
||||
digits_test(record, digits, field_name)
|
||||
else:
|
||||
for record in records:
|
||||
digits_test(getattr(record, field_name),
|
||||
field.digits, field_name)
|
||||
digits_test(record, field.digits, field_name)
|
||||
|
||||
if hasattr(field, 'forbidden_chars'):
|
||||
for record in records:
|
||||
|
||||
@@ -338,7 +338,10 @@ class ModuleTestCase(unittest.TestCase):
|
||||
fields = set()
|
||||
fields |= get_eval_fields(field.domain)
|
||||
if hasattr(field, 'digits'):
|
||||
fields |= get_eval_fields(field.digits)
|
||||
if isinstance(field.digits, str):
|
||||
fields.add(field.digits)
|
||||
else:
|
||||
fields |= get_eval_fields(field.digits)
|
||||
if hasattr(field, 'add_remove'):
|
||||
fields |= get_eval_fields(field.add_remove)
|
||||
if hasattr(field, 'size'):
|
||||
|
||||
Reference in New Issue
Block a user