Add support of domain for non-relation field
issue3743 review4181002
This commit is contained in:
1 parent
64421ff64f
commit
a130816cbd
7 files changed
+68
-67
No files matched your search
@@ -1,3 +1,4 @@
|
||||
* Add support of domain for non-relation field
|
||||
* Manage microseconds in JSON-RPC and XML-RPC
|
||||
* Remove Sha field
|
||||
* Add password widget
|
||||
|
||||
@@ -49,12 +49,6 @@ If ``True``, the field is not editable in the client. Default is ``False``.
|
||||
A :ref:`domain <topics-domain>` constraint that will be applied on the field
|
||||
value.
|
||||
|
||||
.. warning::
|
||||
|
||||
For now it only works on relational fields like :class:`Many2One`,
|
||||
:class:`One2Many` and :class:`Many2Many`.
|
||||
..
|
||||
|
||||
``states``
|
||||
----------
|
||||
|
||||
|
||||
@@ -276,6 +276,7 @@ class Model(WarningErrorMixin, URLMixin, PoolBase):
|
||||
'loading',
|
||||
'filename',
|
||||
'selection_change_with',
|
||||
'domain',
|
||||
):
|
||||
if getattr(cls._fields[field], arg, None) is not None:
|
||||
value = getattr(cls._fields[field], arg)
|
||||
@@ -352,7 +353,6 @@ class Model(WarningErrorMixin, URLMixin, PoolBase):
|
||||
relation = copy.copy(
|
||||
cls._fields[field].get_target().__name__)
|
||||
res[field]['relation'] = relation
|
||||
res[field]['domain'] = copy.copy(cls._fields[field].domain)
|
||||
res[field]['context'] = copy.copy(cls._fields[field].context)
|
||||
res[field]['create'] = accesses.get(field, {}).get('create',
|
||||
True)
|
||||
|
||||
@@ -920,6 +920,51 @@ class ModelStorage(Model):
|
||||
return True
|
||||
return False
|
||||
|
||||
def validate_domain(field):
|
||||
if not field.domain:
|
||||
return
|
||||
if field._type in ('many2one', 'one2many'):
|
||||
Relation = pool.get(field.model_name)
|
||||
elif field._type in ('many2many', 'one2one'):
|
||||
Relation = field.get_target()
|
||||
else:
|
||||
Relation = cls
|
||||
if is_pyson(field.domain):
|
||||
pyson_domain = PYSONEncoder().encode(field.domain)
|
||||
for record in records:
|
||||
env = EvalEnvironment(record, cls)
|
||||
env.update(Transaction().context)
|
||||
env['current_date'] = datetime.datetime.today()
|
||||
env['time'] = time
|
||||
env['context'] = Transaction().context
|
||||
env['active_id'] = record.id
|
||||
domain = PYSONDecoder(env).decode(pyson_domain)
|
||||
validate_relation_domain(
|
||||
field, [record], Relation, domain)
|
||||
else:
|
||||
validate_relation_domain(
|
||||
field, records, Relation, field.domain)
|
||||
|
||||
def validate_relation_domain(field, records, Relation, domain):
|
||||
if Relation != cls:
|
||||
relations = []
|
||||
for record in records:
|
||||
if getattr(record, field.name):
|
||||
if field._type in ('many2one', 'one2one'):
|
||||
relations.append(getattr(record, field.name))
|
||||
else:
|
||||
relations.extend(getattr(record, field.name))
|
||||
else:
|
||||
relations = records
|
||||
if relations:
|
||||
finds = Relation.search(['AND',
|
||||
[('id', 'in', [r.id for r in relations])],
|
||||
domain,
|
||||
])
|
||||
if set(relations) != set(finds):
|
||||
cls.raise_user_error('domain_validation_record',
|
||||
error_args=cls._get_error_args(field.name))
|
||||
|
||||
field_names = set(field_names or [])
|
||||
ctx_pref['active_test'] = False
|
||||
with Transaction().set_context(ctx_pref):
|
||||
@@ -931,65 +976,8 @@ class ModelStorage(Model):
|
||||
if isinstance(field, fields.Function) and \
|
||||
not field.setter:
|
||||
continue
|
||||
# validate domain
|
||||
if (field._type in
|
||||
('many2one', 'many2many', 'one2many', 'one2one')
|
||||
and field.domain):
|
||||
if field._type in ('many2one', 'one2many'):
|
||||
Relation = pool.get(field.model_name)
|
||||
else:
|
||||
Relation = field.get_target()
|
||||
if is_pyson(field.domain):
|
||||
pyson_domain = PYSONEncoder().encode(field.domain)
|
||||
for record in records:
|
||||
env = EvalEnvironment(record, cls)
|
||||
env.update(Transaction().context)
|
||||
env['current_date'] = datetime.datetime.today()
|
||||
env['time'] = time
|
||||
env['context'] = Transaction().context
|
||||
env['active_id'] = record.id
|
||||
domain = PYSONDecoder(env).decode(pyson_domain)
|
||||
relation_ids = []
|
||||
if getattr(record, field_name):
|
||||
if field._type in ('many2one', 'one2one'):
|
||||
relation_ids.append(
|
||||
getattr(record, field_name).id)
|
||||
else:
|
||||
relation_ids.extend(
|
||||
[x.id for x in getattr(record,
|
||||
field_name)])
|
||||
if relation_ids and not Relation.search([
|
||||
'AND',
|
||||
[('id', 'in', relation_ids)],
|
||||
domain,
|
||||
]):
|
||||
cls.raise_user_error(
|
||||
'domain_validation_record',
|
||||
error_args=cls._get_error_args(
|
||||
field_name))
|
||||
else:
|
||||
relation_ids = []
|
||||
for record in records:
|
||||
if getattr(record, field_name):
|
||||
if field._type in ('many2one', 'one2one'):
|
||||
relation_ids.append(
|
||||
getattr(record, field_name).id)
|
||||
else:
|
||||
relation_ids.extend(
|
||||
[x.id for x in getattr(record,
|
||||
field_name)])
|
||||
if relation_ids:
|
||||
finds = Relation.search([
|
||||
'AND',
|
||||
[('id', 'in', relation_ids)],
|
||||
field.domain,
|
||||
])
|
||||
find_ids = map(int, finds)
|
||||
if not set(relation_ids) == set(find_ids):
|
||||
cls.raise_user_error(
|
||||
'domain_validation_record',
|
||||
error_args=cls._get_error_args(
|
||||
field_name))
|
||||
|
||||
validate_domain(field)
|
||||
|
||||
def required_test(value, field_name):
|
||||
if (isinstance(value, (type(None), type(False), list,
|
||||
|
||||
@@ -22,6 +22,7 @@ def register():
|
||||
Integer,
|
||||
IntegerDefault,
|
||||
IntegerRequired,
|
||||
IntegerDomain,
|
||||
Float,
|
||||
FloatDefault,
|
||||
FloatRequired,
|
||||
|
||||
@@ -8,7 +8,7 @@ from trytond.pyson import Eval
|
||||
|
||||
__all__ = [
|
||||
'Boolean', 'BooleanDefault',
|
||||
'Integer', 'IntegerDefault', 'IntegerRequired',
|
||||
'Integer', 'IntegerDefault', 'IntegerRequired', 'IntegerDomain',
|
||||
'Float', 'FloatDefault', 'FloatRequired', 'FloatDigits',
|
||||
'Numeric', 'NumericDefault', 'NumericRequired', 'NumericDigits',
|
||||
'Char', 'CharDefault', 'CharRequired', 'CharSize', 'CharTranslate',
|
||||
@@ -79,6 +79,12 @@ class IntegerRequired(ModelSQL):
|
||||
required=True)
|
||||
|
||||
|
||||
class IntegerDomain(ModelSQL):
|
||||
'Integer Domain'
|
||||
__name__ = 'test.integer_domain'
|
||||
integer = fields.Integer('Integer', domain=[('integer', '>', 42)])
|
||||
|
||||
|
||||
class Float(ModelSQL):
|
||||
'Float'
|
||||
__name__ = 'test.float'
|
||||
|
||||
@@ -29,6 +29,7 @@ class FieldsTestCase(unittest.TestCase):
|
||||
self.integer = POOL.get('test.integer')
|
||||
self.integer_default = POOL.get('test.integer_default')
|
||||
self.integer_required = POOL.get('test.integer_required')
|
||||
self.integer_domain = POOL.get('test.integer_domain')
|
||||
|
||||
self.float = POOL.get('test.float')
|
||||
self.float_default = POOL.get('test.float_default')
|
||||
@@ -389,6 +390,16 @@ class FieldsTestCase(unittest.TestCase):
|
||||
|
||||
transaction.cursor.rollback()
|
||||
|
||||
def test0021integer(self):
|
||||
'Test Integer with domain'
|
||||
with Transaction().start(DB_NAME, USER, context=CONTEXT):
|
||||
self.integer_domain.create([{
|
||||
'integer': 100,
|
||||
}])
|
||||
self.assertRaises(UserError, self.integer_domain.create, [{
|
||||
'integer': 10,
|
||||
}])
|
||||
|
||||
def test0030float(self):
|
||||
'Test Float'
|
||||
with Transaction().start(DB_NAME, USER,
|
||||
|
||||
Reference in new issue
Block a user