Add selection_change_with on Selection and Reference fields

review587002
This commit is contained in:
Cédric Krier committed 2013-02-05 19:06:16 +01:00
1 parent b2d11b6df5
commit d49fb4318d
11 files changed
+141 -17

No files matched your search

+1
View File
@@ -1,3 +1,4 @@
* Add selection_change_with on Selection and Reference fields
* Add Dict fields
* Remove unique constraint on model and field access
* Use lists of values in create
+14 -3
View File
@@ -310,7 +310,7 @@ A binary field. It will be represented in Python by a ``str`` instance.
Selection
---------
.. class:: Selection(selection, string[, sort[, translate[, \**options]]])
.. class:: Selection(selection, string[, sort[, selection_change_with[, translate[, \**options]]])
A string field with limited values to choice.
@@ -328,7 +328,7 @@ A string field with limited values to choice.
The first element in each tuple is the actual value stored. The second
element is the human-readable name.
It can also be the name of a method on the model, that will return an
It can also be the name of a class method on the model, that will return an
appropriate list. The signature of the method is::
selection()
@@ -345,6 +345,13 @@ A string field with limited values to choice.
If true, the choices will be sorted by human-readable value. Default value
is ``True``.
.. attribute:: Selection.selection_change_with
A list of field names. If this attribute is set, the client will call the
``selection`` method of the model when the user changes on of the fields
defined in the list and will give the values of each fields in the list.
The ``selection`` method should be an instance method.
.. attribute:: Selection.translate_selection
If true, the human-readable values will be translated. Default value is
@@ -353,7 +360,7 @@ A string field with limited values to choice.
Reference
---------
.. class:: Reference(string[, selection[, \**options]])
.. class:: Reference(string[, selection[, selection_change_with[, \**options]])
A field that refers to a record of a model. It will be represented in Python by
a ``str`` instance like this::
@@ -368,6 +375,10 @@ But a ``tuple`` can be used to search or set value.
Same as :attr:`Selection.selection` but only for model name.
.. attribute:: Reference.selection_change_with
Same like :attr:`Selection.selection_change_with`.
Many2One
--------
+1
View File
@@ -965,6 +965,7 @@ class TranslationClean(Wizard):
'foreign_model_exist',
'search_function_missing',
'time_format_validation_record',
'selection_value_notfound',
):
return False
if model_name in pool.object_name_list():
+10
View File
@@ -229,6 +229,16 @@ this repository contains the full copyright notices and license terms. -->
<field name="module">ir</field>
<field name="fuzzy" eval="False"/>
</record>
<record model="ir.translation"
id="translation_selection_value_notfound">
<field name="name">selection_value_notfound</field>
<field name="lang">en_US</field>
<field name="type">error</field>
<field name="src">Value not in the selection for field "%s".</field>
<field name="value">Value not in the selection for field "%s".</field>
<field name="module">ir</field>
<field name="fuzzy" eval="False"/>
</record>
<record model="ir.ui.view" id="translation_set_report_start_view_form">
<field name="model">ir.translation.set_report.start</field>
+4 -3
View File
@@ -14,9 +14,9 @@ class Reference(Field):
'''
_type = 'reference'
def __init__(self, string='', selection=None, help='', required=False,
readonly=False, domain=None, states=None, select=False,
on_change=None, on_change_with=None, depends=None,
def __init__(self, string='', selection=None, selection_change_with=None,
help='', required=False, readonly=False, domain=None, states=None,
select=False, on_change=None, on_change_with=None, depends=None,
order_field=None, context=None, loading='lazy'):
'''
:param selection: A list or a function name that returns a list.
@@ -29,6 +29,7 @@ class Reference(Field):
depends=depends, order_field=order_field, context=context,
loading=loading)
self.selection = selection or None
self.selection_change_with = selection_change_with
__init__.__doc__ += Field.__init__.__doc__
+5 -3
View File
@@ -10,8 +10,9 @@ class Selection(Field):
'''
_type = 'selection'
def __init__(self, selection, string='', sort=True, translate=True,
help='', required=False, readonly=False, domain=None, states=None,
def __init__(self, selection, string='', sort=True,
selection_change_with=None, translate=True, help='',
required=False, readonly=False, domain=None, states=None,
select=False, on_change=None, on_change_with=None, depends=None,
order_field=None, context=None, loading='eager'):
'''
@@ -20,7 +21,7 @@ class Selection(Field):
to store and the second is the value to display.
:param sort: A boolean to sort or not the selections.
'''
super(Selection, self).__init__(string=string, help=help,
super(Selection, 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,
depends=depends, order_field=order_field, context=context,
@@ -29,6 +30,7 @@ class Selection(Field):
self.selection = selection.copy()
else:
self.selection = selection
self.selection_change_with = selection_change_with
self.sort = sort
self.translate_selection = translate
__init__.__doc__ += Field.__init__.__doc__
+3
View File
@@ -475,6 +475,7 @@ class Model(WarningErrorMixin, URLMixin):
'datetime_field',
'loading',
'filename',
'selection_change_with',
):
if getattr(cls._fields[field], arg, None) is not None:
res[field][arg] = copy.copy(getattr(cls._fields[field],
@@ -563,6 +564,8 @@ class Model(WarningErrorMixin, URLMixin):
cls._fields[field].field)
if res[field]['type'] in ('datetime', 'time'):
res[field]['format'] = copy.copy(cls._fields[field].format)
if res[field]['type'] == 'selection':
res[field]['context'] = copy.copy(cls._fields[field].context)
if res[field]['type'] == 'dict':
res[field]['context'] = copy.copy(cls._fields[field].context)
res[field]['create'] = accesses.get(field, {}).get('create',
+18 -7
View File
@@ -927,6 +927,13 @@ class ModelStorage(Model):
if isinstance(i, (list, tuple)):
if is_pyson(i):
return True
if isinstance(test, dict):
for key, value in test.items():
if isinstance(value, PYSON):
return True
if isinstance(value, (list, tuple, dict)):
if is_pyson(value):
return True
return False
with Transaction().set_context(ctx_pref):
@@ -1081,13 +1088,6 @@ class ModelStorage(Model):
if hasattr(field, 'selection') and field.selection:
if isinstance(field.selection, (tuple, list)):
test = set(dict(field.selection).keys())
else:
test = set(dict(getattr(cls,
field.selection)()).keys())
# None and '' are equivalent
if '' in test or None in test:
test.add('')
test.add(None)
for record in records:
value = getattr(record, field_name)
if field._type == 'reference':
@@ -1095,6 +1095,17 @@ class ModelStorage(Model):
value = value.__class__.__name__
elif value:
value, _ = value.split(',')
if not isinstance(field.selection, (tuple, list)):
sel_func = getattr(cls, field.selection)
if field.selection_change_with:
test = sel_func(record)
else:
test = sel_func()
test = set(dict(test))
# None and '' are equivalent
if '' in test or None in test:
test.add('')
test.add(None)
if value not in test:
cls.raise_user_error('selection_validation_record',
error_args=cls._get_error_args(field_name))
+2
View File
@@ -83,6 +83,8 @@ def register():
ReferenceTarget,
ReferenceRequired,
Property,
Selection,
SelectionRequired,
Dict,
DictDefault,
DictRequired,
+31
View File
@@ -5,6 +5,7 @@ import datetime
from decimal import Decimal
from trytond.model import ModelSQL, fields
from trytond.pyson import Eval
from trytond.transaction import Transaction
__all__ = [
'Boolean', 'BooleanDefault',
@@ -32,6 +33,7 @@ __all__ = [
'Many2ManySize', 'Many2ManySizeTarget', 'Many2ManySizeRelation',
'Reference', 'ReferenceTarget', 'ReferenceRequired',
'Property',
'Selection', 'SelectionRequired',
'Dict', 'DictDefault', 'DictRequired',
]
@@ -589,6 +591,35 @@ class Property(ModelSQL):
], 'Test Selection'))
class Selection(ModelSQL):
'Selection'
__name__ = 'test.selection'
select = fields.Selection([
('', ''), ('arabic', 'Arabic'), ('hexa', 'Hexadecimal')],
'Selection')
dyn_select = fields.Selection('get_selection',
'Instance Dynamic Selection', selection_change_with=['select'])
dyn_select_static = fields.Selection('static_selection',
'Static Selection')
def get_selection(self):
if self.select == 'arabic':
return [('', '')] + [(str(i), str(i)) for i in range(1, 11)]
else:
return [('', '')] + [(hex(i), hex(i)) for i in range(1, 11)]
@staticmethod
def static_selection():
return [('', '')] + [(str(i), str(i)) for i in range(1, 11)]
class SelectionRequired(ModelSQL):
'Selection Required'
__name__ = 'test.selection_required'
select = fields.Selection([('arabic', 'Arabic'), ('latin', 'Latin')],
'Selection', required=True)
class Dict(ModelSQL):
'Dict'
__name__ = 'test.dict'
+52 -1
View File
@@ -102,6 +102,9 @@ class FieldsTestCase(unittest.TestCase):
self.ir_property = POOL.get('ir.property')
self.model_field = POOL.get('ir.model.field')
self.selection = POOL.get('test.selection')
self.selection_required = POOL.get('test.selection_required')
self.dict_ = POOL.get('test.dict')
self.dict_default = POOL.get('test.dict_default')
self.dict_required = POOL.get('test.dict_required')
@@ -3133,7 +3136,55 @@ class FieldsTestCase(unittest.TestCase):
transaction.cursor.rollback()
def test0160dict(self):
def test_0160selection(self):
with Transaction().start(DB_NAME, USER,
context=CONTEXT) as transaction:
selection1, = self.selection.create([{'select': 'arabic'}])
self.assert_(selection1)
self.assertEqual(selection1.select, 'arabic')
selection2, = self.selection.create([{'select': None}])
self.assert_(selection2)
self.assertEqual(selection2.select, None)
self.assertRaises(Exception, self.selection.create,
[{'select': 'chinese'}])
selection3, = self.selection.create(
[{'select': 'arabic', 'dyn_select': '1'}])
self.assert_(selection3)
self.assertEqual(selection3.select, 'arabic')
self.assertEqual(selection3.dyn_select, '1')
selection4, = self.selection.create(
[{'select': 'hexa', 'dyn_select': '0x3'}])
self.assert_(selection4)
self.assertEqual(selection4.select, 'hexa')
self.assertEqual(selection4.dyn_select, '0x3')
selection5, = self.selection.create(
[{'select': 'hexa', 'dyn_select': None}])
self.assert_(selection5)
self.assertEqual(selection5.select, 'hexa')
self.assertEqual(selection5.dyn_select, None)
self.assertRaises(Exception, self.selection.create,
[{'select': 'arabic', 'dyn_select': '0x3'}])
self.assertRaises(Exception, self.selection.create,
[{'select': 'hexa', 'dyn_select': '3'}])
self.assertRaises(Exception, self.selection_required.create, [{}])
transaction.cursor.rollback()
self.assertRaises(Exception, self.selection_required.create,
[{'select': None}])
transaction.cursor.rollback()
selection6, = self.selection_required.create([{'select': 'latin'}])
self.assert_(selection6)
self.assertEqual(selection6.select, 'latin')
def test0170dict(self):
with Transaction().start(DB_NAME, USER,
context=CONTEXT) as transaction:
dict1, = self.dict_.create([{