diff --git a/CHANGELOG b/CHANGELOG index 50b75944c8..f900d2ba11 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,5 @@ +* Remove Property field +* Add MultiValueMixin and ValueMixin * Use sql type in column creation * Use generic SQL type in field and let backend determine the SQLType * Add filter to xxx2Many fields diff --git a/doc/ref/models/fields.rst b/doc/ref/models/fields.rst index 754ec470c1..7b3ceca575 100644 --- a/doc/ref/models/fields.rst +++ b/doc/ref/models/fields.rst @@ -819,29 +819,25 @@ Instance methods: :class:`~trytond.model.Model` instance of the field, `name` is the name of the field, `clause` is a clause of :ref:`domain `. -Property --------- +MultiValue +---------- -.. class:: Property(field) +.. class:: MultiValue(field) -A property field that is like a :class:`Function` field but with predifined -:attr:`~Function.getter`, :attr:`~Function.setter` and -:attr:`~Function.searcher` that use the :class:`~trytond.model.ModelSQL` -`ir.property` to store values. +A multivalue field that is like a :class:`Function` field but with predefined +:attr:`~Function.getter` and :attr:`~Function.setter` that use the +:class:`~trytond.model.MultiValueMixin` for stored values. -Instance methods: +.. warning:: + The :meth:`~trytond.model.MultiValueMixin.get_multivalue` and + :meth:`~trytond.model.MultiValueMixin.set_multivalue` should be prefered + over the descriptors of the field. +.. -.. method:: Property.get(ids, model, name[, values]) - - Same as :meth:`Function.get`. - -.. method:: Property.set(ids, model, name, value) - - Same as :meth:`Function.set`. - -.. method:: Property.search(model, name, clause) - - Same as :meth:`Function.search`. +.. warning:: + The :ref:`default ` method of the field must + accept pattern as keyword argument. +.. Dict ---- diff --git a/doc/ref/models/models.rst b/doc/ref/models/models.rst index b8672a323f..5f59d166d5 100644 --- a/doc/ref/models/models.rst +++ b/doc/ref/models/models.rst @@ -647,9 +647,10 @@ record is equal or not defined. Instance methods: -.. method:: MatchMixin.match(pattern) +.. method:: MatchMixin.match(pattern[, match_none]) - Return if the instance match the pattern + Return if the instance match the pattern. If `match_none` is set `None` + value of the instance will be compared. ========== UnionMixin @@ -697,6 +698,60 @@ the field to be created and its default values is `sequence`. field_label defines the label which will be used by the field and defaults to `Sequence`. Order specifies the order direction and defaults to `ASC NULLS FIRST`. +=============== +MultiValueMixin +=============== + +.. class:: MultiValueMixin + +A mixin_ for :class:`Model` to help having +:class:`trytond.model.fields.MultiValue` fields with multi-values on a +:class:`ValueMixin`. The values are stored by creating one record per pattern. +The patterns are the same as those on :class:`MatchMixin`. + +Class methods: + +.. classmethod:: MultiValueMixin.multivalue_model(field) + + Return the :class:`ValueMixin` on which the values are stored for the + field name. The default is class name suffixed by the field name. + +.. classmethod:: MultiValueMixin.setter_multivalue(records, name, value, \*\*pattern) + + The setter method for the :class:`trytond.model.fields.Function` fields. + +Instance methods: + +.. method:: MultiValueMixin.multivalue_records(field) + + Return the list of all :class:`ValueMixin` records linked to the instance. + By default, it returns the value of the first found + :class:`trytond.model.fields.One2Many` linked to the multivalue model or + all the records of this one. + +.. method:: MultiValueMixin.multivalue_record(field, \*\*pattern) + + Return a new record of :class:`ValueMixin` linked to the instance. + +.. method:: MultiValueMixin.get_multivalue(name, \*\*pattern) + + Return the value of the field `name` for the pattern. + +.. method:: MultiValueMixin.set_multivalue(name, value, \*\*pattern) + + Store the value of the field `name` for the pattern. + +.. warning:: + To customize the pattern, both methods must be override the same way. +.. + +========== +ValueMixin +========== + +.. class:: ValueMixin + +A mixin_ to store the values of :class:`MultiValueMixin`. .. _mixin: http://en.wikipedia.org/wiki/Mixin .. _JSON: http://en.wikipedia.org/wiki/Json diff --git a/trytond/ir/__init__.py b/trytond/ir/__init__.py index b4a8ff47e6..1e0a5aedc1 100644 --- a/trytond/ir/__init__.py +++ b/trytond/ir/__init__.py @@ -7,7 +7,6 @@ from .sequence import * from .ui.menu import * from .ui.view import * from .ui.icon import * -from .property import * from .action import * from .model import * from .attachment import * @@ -45,7 +44,6 @@ def register(): ViewTreeState, ViewSearch, Icon, - Property, Action, ActionKeyword, ActionReport, diff --git a/trytond/ir/model.py b/trytond/ir/model.py index c43e1afa7a..6b074fd0b8 100644 --- a/trytond/ir/model.py +++ b/trytond/ir/model.py @@ -77,8 +77,6 @@ class Model(ModelSQL, ModelView): @classmethod def register(cls, model, module_name): - pool = Pool() - Property = pool.get('ir.property') cursor = Transaction().connection.cursor() ir_model = cls.__table__() @@ -97,7 +95,6 @@ class Model(ModelSQL, ModelView): ir_model.module], [[model.__name__, model._get_name(), model.__doc__, module_name]])) - Property._models_get_cache.clear() cursor.execute(*ir_model.select(ir_model.id, where=ir_model.model == model.__name__)) (model_id,) = cursor.fetchone() @@ -124,31 +121,6 @@ class Model(ModelSQL, ModelView): return [name for name, model in Pool().iterobject() if getattr(model, '_history', False)] - @classmethod - def create(cls, vlist): - pool = Pool() - Property = pool.get('ir.property') - res = super(Model, cls).create(vlist) - # Restart the cache of models_get - Property._models_get_cache.clear() - return res - - @classmethod - def write(cls, models, values, *args): - pool = Pool() - Property = pool.get('ir.property') - super(Model, cls).write(models, values, *args) - # Restart the cache of models_get - Property._models_get_cache.clear() - - @classmethod - def delete(cls, models): - pool = Pool() - Property = pool.get('ir.property') - super(Model, cls).delete(models) - # Restart the cache of models_get - Property._models_get_cache.clear() - @classmethod def global_search(cls, text, limit, menu='ir.ui.menu'): """ diff --git a/trytond/ir/property.py b/trytond/ir/property.py deleted file mode 100644 index ead53e92ea..0000000000 --- a/trytond/ir/property.py +++ /dev/null @@ -1,173 +0,0 @@ -# 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 decimal import Decimal -from ..model import ModelView, ModelSQL, fields -from ..transaction import Transaction -from ..cache import Cache -from ..pool import Pool - -__all__ = [ - 'Property', - ] - -_CAST = { - 'numeric': Decimal, - 'integer': int, - 'float': float, - } - - -class Property(ModelSQL, ModelView): - "Property" - __name__ = 'ir.property' - _rec_name = 'field' - value = fields.Reference('Value', selection='models_get') - res = fields.Reference('Resource', selection='models_get', select=True) - field = fields.Many2One('ir.model.field', 'Field', - ondelete='CASCADE', required=True, select=True) - _models_get_cache = Cache('ir_property.models_get', context=False) - - @classmethod - def models_get(cls): - pool = Pool() - Model = pool.get('ir.model') - models = cls._models_get_cache.get(None) - if models: - return models - cursor = Transaction().connection.cursor() - model = Model.__table__() - cursor.execute(*model.select(model.model, model.name, - order_by=model.name.asc)) - models = cursor.fetchall() + [('', '')] - cls._models_get_cache.set(None, models) - return models - - @classmethod - def get(cls, names, model, res_ids=None): - """ - Return named property values for each res_ids of model - """ - pool = Pool() - ModelAccess = pool.get('ir.model.access') - res = {} - - ModelAccess.check(model, 'read') - - names_list = True - if not isinstance(names, list): - names_list = False - names = [names] - if res_ids is None: - res_ids = [] - - properties = cls.search([ - ('field.name', 'in', names), - ['OR', - ('res', '=', None), - ('res', 'in', ['%s,%s' % (model, x) for x in res_ids]), - ], - ], order=[]) - - default_vals = dict((x, None) for x in names) - for property_ in (x for x in properties if not x.res): - value = property_.value - val = None - if value is not None: - if not isinstance(value, basestring): - val = int(value) - else: - if property_.field.ttype in _CAST: - cast = _CAST[property_.field.ttype] - val = cast(value.split(',')[1]) - elif property_.field.ttype in ('char', 'selection'): - val = value.split(',')[1] - else: - raise Exception('Not implemented') - default_vals[property_.field.name] = val - - if not res_ids: - if not names_list: - return default_vals[names[0]] - return default_vals - - for name in names: - res[name] = dict((x, default_vals[name]) for x in res_ids) - - for property_ in (x for x in properties if x.res): - val = None - if property_.value is not None: - if not isinstance(property_.value, basestring): - val = int(property_.value) - else: - if property_.field.ttype in _CAST: - cast = _CAST[property_.field.ttype] - val = cast(property_.value.split(',')[1]) - elif property_.field.ttype in ('char', 'selection'): - val = property_.value.split(',')[1] - else: - raise Exception('Not implemented') - res[property_.field.name][int(property_.res)] = val - - if not names_list: - return res[names[0]] - return res - - @staticmethod - def _set_values(model, res_id, val, field_id): - return { - 'value': val, - 'res': model + ',' + str(res_id), - 'field': field_id, - } - - @classmethod - def set(cls, name, model, ids, val): - """ - Set named property value for ids of model - Return the id of the record created - """ - pool = Pool() - ModelField = pool.get('ir.model.field') - ModelAccess = pool.get('ir.model.access') - - ModelAccess.check(model, 'write') - - model_field, = ModelField.search([ - ('name', '=', name), - ('model.model', '=', model), - ], order=[], limit=1) - Model = pool.get(model) - field = Model._fields[name] - - properties = cls.search([ - ('field', '=', model_field.id), - ('res', 'in', [model + ',' + str(res_id) for res_id in ids]), - ], order=[]) - cls.delete(properties) - - defaults = cls.search([ - ('field', '=', model_field.id), - ('res', '=', None), - ], order=[], limit=1) - default_val = None - if defaults: - value = cls(defaults[0].id).value - default_val = None - if value is not None: - if not isinstance(value, basestring): - default_val = int(value) - else: - if field._type in _CAST: - cast = _CAST[field._type] - default_val = cast(value.split(',')[1]) - elif field._type in ('char', 'selection'): - default_val = value.split(',')[1] - else: - raise Exception('Not implemented') - - if (val != default_val): - to_create = [] - for res_id in ids: - vals = cls._set_values(model, res_id, val, model_field.id) - to_create.append(vals) - cls.create(to_create) diff --git a/trytond/ir/property.xml b/trytond/ir/property.xml deleted file mode 100644 index 2ea792e1be..0000000000 --- a/trytond/ir/property.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - ir.property - form - property_form - - - ir.property - tree - property_list - - - Default Properties - ir.action.act_window - ir.property - - - - - - - - - - - - - - - Properties - ir.action.act_window - ir.property - - - - - - - - - - - - - - diff --git a/trytond/ir/tryton.cfg b/trytond/ir/tryton.cfg index 152b3e1e20..446f50c052 100644 --- a/trytond/ir/tryton.cfg +++ b/trytond/ir/tryton.cfg @@ -15,6 +15,5 @@ xml: translation.xml export.xml rule.xml - property.xml module.xml trigger.xml diff --git a/trytond/ir/view/property_form.xml b/trytond/ir/view/property_form.xml deleted file mode 100644 index 5a760c3f0c..0000000000 --- a/trytond/ir/view/property_form.xml +++ /dev/null @@ -1,11 +0,0 @@ - - -
-