1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2013-2014 OpenERP (<http://www.openerp.com>).
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as
9 # published by the Free Software Foundation, either version 3 of the
10 # License, or (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 ##############################################################################
22 """ High-level objects for fields. """
25 from datetime import date, datetime
26 from functools import partial
27 from operator import attrgetter
32 from types import NoneType
34 from openerp.tools import float_round, ustr, html_sanitize
35 from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as DATE_FORMAT
36 from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT as DATETIME_FORMAT
38 DATE_LENGTH = len(date.today().strftime(DATE_FORMAT))
39 DATETIME_LENGTH = len(datetime.now().strftime(DATETIME_FORMAT))
41 _logger = logging.getLogger(__name__)
43 class SpecialValue(object):
44 """ Encapsulates a value in the cache in place of a normal value. """
45 def __init__(self, value):
50 class FailedValue(SpecialValue):
51 """ Special value that encapsulates an exception instead of a value. """
52 def __init__(self, exception):
53 self.exception = exception
57 def _check_value(value):
58 """ Return `value`, or call its getter if `value` is a :class:`SpecialValue`. """
59 return value.get() if isinstance(value, SpecialValue) else value
62 def resolve_all_mro(cls, name, reverse=False):
63 """ Return the (successively overridden) values of attribute `name` in `cls`
64 in mro order, or inverse mro order if `reverse` is true.
66 klasses = reversed(cls.__mro__) if reverse else cls.__mro__
68 if name in klass.__dict__:
69 yield klass.__dict__[name]
72 def default_compute(field, value):
73 """ Return a compute function for the given default `value`; `value` is
74 either a constant, or a unary function returning the default value.
77 func = value if callable(value) else lambda rec: value
84 class MetaField(type):
85 """ Metaclass for field classes. """
88 def __init__(cls, name, bases, attrs):
89 super(MetaField, cls).__init__(name, bases, attrs)
91 cls.by_type[cls.type] = cls
93 # compute class attributes to avoid calling dir() on fields
95 cls.related_attrs = []
96 cls.description_attrs = []
98 if attr.startswith('_column_'):
99 cls.column_attrs.append((attr[8:], attr))
100 elif attr.startswith('_related_'):
101 cls.related_attrs.append((attr[9:], attr))
102 elif attr.startswith('_description_'):
103 cls.description_attrs.append((attr[13:], attr))
107 """ The field descriptor contains the field definition, and manages accesses
108 and assignments of the corresponding field on records. The following
109 attributes may be provided when instanciating a field:
111 :param string: the label of the field seen by users (string); if not
112 set, the ORM takes the field name in the class (capitalized).
114 :param help: the tooltip of the field seen by users (string)
116 :param readonly: whether the field is readonly (boolean, by default ``False``)
118 :param required: whether the value of the field is required (boolean, by
121 :param index: whether the field is indexed in database (boolean, by
124 :param default: the default value for the field; this is either a static
125 value, or a function taking a recordset and returning a value
127 :param states: a dictionary mapping state values to lists of UI attribute-value
128 pairs; possible attributes are: 'readonly', 'required', 'invisible'.
129 Note: Any state-based condition requires the ``state`` field value to be
130 available on the client-side UI. This is typically done by including it in
131 the relevant views, possibly made invisible if not relevant for the
134 :param groups: comma-separated list of group xml ids (string); this
135 restricts the field access to the users of the given groups only
137 :param bool copy: whether the field value should be copied when the record
138 is duplicated (default: ``True`` for normal fields, ``False`` for
139 ``one2many`` and computed fields, including property fields and
144 .. rubric:: Computed fields
146 One can define a field whose value is computed instead of simply being
147 read from the database. The attributes that are specific to computed
148 fields are given below. To define such a field, simply provide a value
149 for the attribute `compute`.
151 :param compute: name of a method that computes the field
153 :param inverse: name of a method that inverses the field (optional)
155 :param search: name of a method that implement search on the field (optional)
157 :param store: whether the field is stored in database (boolean, by
158 default ``False`` on computed fields)
160 The methods given for `compute`, `inverse` and `search` are model
161 methods. Their signature is shown in the following example::
163 upper = fields.Char(compute='_compute_upper',
164 inverse='_inverse_upper',
165 search='_search_upper')
168 def _compute_upper(self):
170 self.upper = self.name.upper() if self.name else False
172 def _inverse_upper(self):
174 self.name = self.upper.lower() if self.upper else False
176 def _search_upper(self, operator, value):
177 if operator == 'like':
179 return [('name', operator, value)]
181 The compute method has to assign the field on all records of the invoked
182 recordset. The decorator :meth:`openerp.api.depends` must be applied on
183 the compute method to specify the field dependencies; those dependencies
184 are used to determine when to recompute the field; recomputation is
185 automatic and guarantees cache/database consistency. Note that the same
186 method can be used for several fields, you simply have to assign all the
187 given fields in the method; the method will be invoked once for all
190 By default, a computed field is not stored to the database, and is
191 computed on-the-fly. Adding the attribute ``store=True`` will store the
192 field's values in the database. The advantage of a stored field is that
193 searching on that field is done by the database itself. The disadvantage
194 is that it requires database updates when the field must be recomputed.
196 The inverse method, as its name says, does the inverse of the compute
197 method: the invoked records have a value for the field, and you must
198 apply the necessary changes on the field dependencies such that the
199 computation gives the expected value. Note that a computed field without
200 an inverse method is readonly by default.
202 The search method is invoked when processing domains before doing an
203 actual search on the model. It must return a domain equivalent to the
204 condition: `field operator value`.
208 .. rubric:: Related fields
210 The value of a related field is given by following a sequence of
211 relational fields and reading a field on the reached model. The complete
212 sequence of fields to traverse is specified by the attribute
214 :param related: sequence of field names
216 The value of some attributes from related fields are automatically taken
217 from the source field, when it makes sense. Examples are the attributes
218 `string` or `selection` on selection fields.
220 By default, the values of related fields are not stored to the database.
221 Add the attribute ``store=True`` to make it stored, just like computed
222 fields. Related fields are automatically recomputed when their
223 dependencies are modified.
225 .. _field-company-dependent:
227 .. rubric:: Company-dependent fields
229 Formerly known as 'property' fields, the value of those fields depends
230 on the company. In other words, users that belong to different companies
231 may see different values for the field on a given record.
233 :param company_dependent: whether the field is company-dependent (boolean)
235 .. _field-incremental-definition:
237 .. rubric:: Incremental definition
239 A field is defined as class attribute on a model class. If the model
240 is extended (see :class:`~openerp.models.Model`), one can also extend
241 the field definition by redefining a field with the same name and same
242 type on the subclass. In that case, the attributes of the field are
243 taken from the parent class and overridden by the ones given in
246 For instance, the second class below only adds a tooltip on the field
249 class First(models.Model):
251 state = fields.Selection([...], required=True)
253 class Second(models.Model):
255 state = fields.Selection(help="Blah blah blah")
258 __metaclass__ = MetaField
260 _attrs = None # dictionary with all field attributes
261 _free_attrs = None # list of semantic-free attribute names
263 automatic = False # whether the field is automatically created ("magic" field)
264 _origin = None # the column or field interfaced by self, if any
266 name = None # name of the field
267 type = None # type of the field (string)
268 relational = False # whether the field is a relational one
269 model_name = None # name of the model of this field
270 comodel_name = None # name of the model of values (if relational)
271 inverse_fields = None # list of inverse fields (objects)
273 store = True # whether the field is stored in database
274 index = False # whether the field is indexed in database
275 manual = False # whether the field is a custom field
276 copyable = True # whether the field is copied over by BaseModel.copy()
277 depends = () # collection of field dependencies
278 recursive = False # whether self depends on itself
279 compute = None # compute(recs) computes field on recs
280 inverse = None # inverse(recs) inverses field on recs
281 search = None # search(recs, operator, value) searches on self
282 related = None # sequence of field names, for related fields
283 company_dependent = False # whether `self` is company-dependent (property field)
284 default = None # default value
286 string = None # field label
287 help = None # field tooltip
291 groups = False # csv list of group xml ids
292 change_default = None # whether the field may trigger a "user-onchange"
293 deprecated = None # whether the field is ... deprecated
295 def __init__(self, string=None, **kwargs):
296 kwargs['string'] = string
297 self._attrs = {key: val for key, val in kwargs.iteritems() if val is not None}
298 self._free_attrs = []
300 def copy(self, **kwargs):
301 """ copy(item) -> test
303 make a copy of `self`, possibly modified with parameters `kwargs` """
305 field._attrs = {key: val for key, val in kwargs.iteritems() if val is not None}
306 field._free_attrs = list(self._free_attrs)
309 def set_class_name(self, cls, name):
310 """ Assign the model class and field name of `self`. """
311 self.model_name = cls._name
314 # determine all inherited field attributes
316 for field in resolve_all_mro(cls, name, reverse=True):
317 if isinstance(field, type(self)):
318 attrs.update(field._attrs)
321 attrs.update(self._attrs) # necessary in case self is not in cls
323 # initialize `self` with `attrs`
324 if attrs.get('compute'):
325 # by default, computed fields are not stored, not copied and readonly
326 attrs['store'] = attrs.get('store', False)
327 attrs['copy'] = attrs.get('copy', False)
328 attrs['readonly'] = attrs.get('readonly', not attrs.get('inverse'))
329 if attrs.get('related'):
330 # by default, related fields are not stored
331 attrs['store'] = attrs.get('store', False)
333 # attribute is copyable because there is also a copy() method
334 attrs['copyable'] = attrs.pop('copy')
336 for attr, value in attrs.iteritems():
337 if not hasattr(self, attr):
338 self._free_attrs.append(attr)
339 setattr(self, attr, value)
342 self.string = name.replace('_', ' ').capitalize()
347 return "%s.%s" % (self.model_name, self.name)
350 return "%s.%s" % (self.model_name, self.name)
352 ############################################################################
358 """ Prepare `self` for a new setup. """
359 self._setup_done = False
360 # self._triggers is a set of pairs (field, path) that represents the
361 # computed fields that depend on `self`. When `self` is modified, it
362 # invalidates the cache of each `field`, and registers the records to
363 # recompute based on `path`. See method `modified` below for details.
364 self._triggers = set()
365 self.inverse_fields = []
367 def setup(self, env):
368 """ Complete the setup of `self` (dependencies, recomputation triggers,
369 and other properties). This method is idempotent: it has no effect
370 if `self` has already been set up.
372 if not self._setup_done:
373 self._setup_done = True
376 def _setup(self, env):
377 """ Do the actual setup of `self`. """
379 self._setup_related(env)
381 self._setup_regular(env)
383 # put invalidation/recomputation triggers on field dependencies
384 model = env[self.model_name]
385 for path in self.depends:
386 self._setup_dependency([], model, path.split('.'))
388 # put invalidation triggers on model dependencies
389 for dep_model_name, field_names in model._depends.iteritems():
390 dep_model = env[dep_model_name]
391 for field_name in field_names:
392 field = dep_model._fields[field_name]
393 field._triggers.add((self, None))
396 # Setup of related fields
399 def _setup_related(self, env):
400 """ Setup the attributes of a related field. """
401 # fix the type of self.related if necessary
402 if isinstance(self.related, basestring):
403 self.related = tuple(self.related.split('.'))
405 # determine the related field, and make sure it is set up
406 recs = env[self.model_name]
407 for name in self.related[:-1]:
409 field = self.related_field = recs._fields[self.related[-1]]
412 # check type consistency
413 if self.type != field.type:
414 raise Warning("Type of related field %s is inconsistent with %s" % (self, field))
416 # determine dependencies, compute, inverse, and search
417 self.depends = ('.'.join(self.related),)
418 self.compute = self._compute_related
419 self.inverse = self._inverse_related
420 if field._description_searchable(env):
421 self.search = self._search_related
423 # copy attributes from field to self (string, help, etc.)
424 for attr, prop in self.related_attrs:
425 if not getattr(self, attr):
426 setattr(self, attr, getattr(field, prop))
428 def _compute_related(self, records):
429 """ Compute the related field `self` on `records`. """
430 for record, sudo_record in zip(records, records.sudo()):
431 # bypass access rights check when traversing the related path
432 value = sudo_record if record.id else record
433 # traverse the intermediate fields, and keep at most one record
434 for name in self.related[:-1]:
435 value = value[name][:1]
436 record[self.name] = value[self.related[-1]]
438 def _inverse_related(self, records):
439 """ Inverse the related field `self` on `records`. """
440 for record in records:
442 # traverse the intermediate fields, and keep at most one record
443 for name in self.related[:-1]:
444 other = other[name][:1]
446 other[self.related[-1]] = record[self.name]
448 def _search_related(self, records, operator, value):
449 """ Determine the domain to search on field `self`. """
450 return [('.'.join(self.related), operator, value)]
452 # properties used by _setup_related() to copy values from related field
453 _related_string = property(attrgetter('string'))
454 _related_help = property(attrgetter('help'))
455 _related_readonly = property(attrgetter('readonly'))
456 _related_groups = property(attrgetter('groups'))
459 # Setup of non-related fields
462 def _setup_regular(self, env):
463 """ Setup the attributes of a non-related field. """
464 recs = env[self.model_name]
466 def make_depends(deps):
467 return tuple(deps(recs) if callable(deps) else deps)
469 # transform self.default into self.compute
470 if self.default is not None and self.compute is None:
471 self.compute = default_compute(self, self.default)
473 # convert compute into a callable and determine depends
474 if isinstance(self.compute, basestring):
475 # if the compute method has been overridden, concatenate all their _depends
477 for method in resolve_all_mro(type(recs), self.compute, reverse=True):
478 self.depends += make_depends(getattr(method, '_depends', ()))
479 self.compute = getattr(type(recs), self.compute)
481 self.depends = make_depends(getattr(self.compute, '_depends', ()))
483 # convert inverse and search into callables
484 if isinstance(self.inverse, basestring):
485 self.inverse = getattr(type(recs), self.inverse)
486 if isinstance(self.search, basestring):
487 self.search = getattr(type(recs), self.search)
489 def _setup_dependency(self, path0, model, path1):
490 """ Make `self` depend on `model`; `path0 + path1` is a dependency of
491 `self`, and `path0` is the sequence of field names from `self.model`
495 head, tail = path1[0], path1[1:]
498 # special case: add triggers on all fields of model (except self)
499 fields = set(model._fields.itervalues()) - set([self])
501 fields = [model._fields[head]]
505 _logger.debug("Field %s is recursively defined", self)
506 self.recursive = True
511 #_logger.debug("Add trigger on %s to recompute %s", field, self)
512 field._triggers.add((self, '.'.join(path0 or ['id'])))
514 # add trigger on inverse fields, too
515 for invf in field.inverse_fields:
516 #_logger.debug("Add trigger on %s to recompute %s", invf, self)
517 invf._triggers.add((self, '.'.join(path0 + [head])))
519 # recursively traverse the dependency
521 comodel = env[field.comodel_name]
522 self._setup_dependency(path0 + [head], comodel, tail)
525 def dependents(self):
526 """ Return the computed fields that depend on `self`. """
527 return (field for field, path in self._triggers)
529 ############################################################################
534 def get_description(self, env):
535 """ Return a dictionary that describes the field `self`. """
536 desc = {'type': self.type}
537 for attr, prop in self.description_attrs:
538 value = getattr(self, prop)
541 if value is not None:
546 # properties used by get_description()
548 def _description_store(self, env):
550 # if the corresponding column is a function field, check the column
551 column = env[self.model_name]._columns.get(self.name)
552 return bool(getattr(column, 'store', True))
555 def _description_searchable(self, env):
556 return self._description_store(env) or bool(self.search)
558 _description_manual = property(attrgetter('manual'))
559 _description_depends = property(attrgetter('depends'))
560 _description_related = property(attrgetter('related'))
561 _description_company_dependent = property(attrgetter('company_dependent'))
562 _description_readonly = property(attrgetter('readonly'))
563 _description_required = property(attrgetter('required'))
564 _description_states = property(attrgetter('states'))
565 _description_groups = property(attrgetter('groups'))
566 _description_change_default = property(attrgetter('change_default'))
567 _description_deprecated = property(attrgetter('deprecated'))
569 def _description_string(self, env):
570 if self.string and env.lang:
571 name = "%s,%s" % (self.model_name, self.name)
572 trans = env['ir.translation']._get_source(name, 'field', env.lang)
573 return trans or self.string
576 def _description_help(self, env):
577 if self.help and env.lang:
578 name = "%s,%s" % (self.model_name, self.name)
579 trans = env['ir.translation']._get_source(name, 'help', env.lang)
580 return trans or self.help
583 ############################################################################
585 # Conversion to column instance
589 """ return a low-level field object corresponding to `self` """
592 assert isinstance(self._origin, fields._column)
595 _logger.debug("Create fields._column for Field %s", self)
597 for attr, prop in self.column_attrs:
598 args[attr] = getattr(self, prop)
599 for attr in self._free_attrs:
600 args[attr] = getattr(self, attr)
602 if self.company_dependent:
603 # company-dependent fields are mapped to former property fields
604 args['type'] = self.type
605 args['relation'] = self.comodel_name
606 return fields.property(**args)
608 return getattr(fields, self.type)(**args)
610 # properties used by to_column() to create a column instance
611 _column_copy = property(attrgetter('copyable'))
612 _column_select = property(attrgetter('index'))
613 _column_manual = property(attrgetter('manual'))
614 _column_string = property(attrgetter('string'))
615 _column_help = property(attrgetter('help'))
616 _column_readonly = property(attrgetter('readonly'))
617 _column_required = property(attrgetter('required'))
618 _column_states = property(attrgetter('states'))
619 _column_groups = property(attrgetter('groups'))
620 _column_change_default = property(attrgetter('change_default'))
621 _column_deprecated = property(attrgetter('deprecated'))
623 ############################################################################
625 # Conversion of values
629 """ return the null value for this field in the given environment """
632 def convert_to_cache(self, value, record, validate=True):
633 """ convert `value` to the cache level in `env`; `value` may come from
634 an assignment, or have the format of methods :meth:`BaseModel.read`
635 or :meth:`BaseModel.write`
637 :param record: the target record for the assignment, or an empty recordset
639 :param bool validate: when True, field-specific validation of
640 `value` will be performed
644 def convert_to_read(self, value, use_name_get=True):
645 """ convert `value` from the cache to a value as returned by method
646 :meth:`BaseModel.read`
648 :param bool use_name_get: when True, value's diplay name will
649 be computed using :meth:`BaseModel.name_get`, if relevant
652 return False if value is None else value
654 def convert_to_write(self, value, target=None, fnames=None):
655 """ convert `value` from the cache to a valid value for method
656 :meth:`BaseModel.write`.
658 :param target: optional, the record to be modified with this value
659 :param fnames: for relational fields only, an optional collection of
660 field names to convert
662 return self.convert_to_read(value)
664 def convert_to_onchange(self, value):
665 """ convert `value` from the cache to a valid value for an onchange
668 return self.convert_to_write(value)
670 def convert_to_export(self, value, env):
671 """ convert `value` from the cache to a valid value for export. The
672 parameter `env` is given for managing translations.
674 if env.context.get('export_raw_data'):
676 return bool(value) and ustr(value)
678 def convert_to_display_name(self, value):
679 """ convert `value` from the cache to a suitable display name. """
682 ############################################################################
687 def __get__(self, record, owner):
688 """ return the value of field `self` on `record` """
690 return self # the field is accessed through the owner class
693 # null record -> return the null value for this field
694 return self.null(record.env)
696 # only a single record may be accessed
700 return record._cache[self]
704 # cache miss, retrieve value
706 # normal record -> read or compute value for this field
707 self.determine_value(record)
709 # new record -> compute default value for this field
710 record.add_default_value(self)
712 # the result should be in cache now
713 return record._cache[self]
715 def __set__(self, record, value):
716 """ set the value of field `self` on `record` """
719 # only a single record may be updated
722 # adapt value to the cache level
723 value = self.convert_to_cache(value, record)
725 if env.in_draft or not record.id:
726 # determine dependent fields
727 spec = self.modified_draft(record)
729 # set value in cache, inverse field, and mark record as dirty
730 record._cache[self] = value
732 for invf in self.inverse_fields:
733 invf._update(value, record)
736 # determine more dependent fields, and invalidate them
738 spec += self.modified_draft(record)
742 # simply write to the database, and update cache
743 record.write({self.name: self.convert_to_write(value)})
744 record._cache[self] = value
746 ############################################################################
748 # Computation of field values
751 def _compute_value(self, records):
752 """ Invoke the compute method on `records`. """
753 # mark the computed fields failed in cache, so that access before
754 # computation raises an exception
755 exc = Warning("Field %s is accessed before being computed." % self)
756 for field in self.computed_fields:
757 records._cache[field] = FailedValue(exc)
758 records.env.computed[field].update(records._ids)
759 self.compute(records)
760 for field in self.computed_fields:
761 records.env.computed[field].difference_update(records._ids)
763 def compute_value(self, records):
764 """ Invoke the compute method on `records`; the results are in cache. """
765 with records.env.do_in_draft():
767 self._compute_value(records)
769 # some record is missing, retry on existing records only
770 self._compute_value(records.exists())
772 def determine_value(self, record):
773 """ Determine the value of `self` for `record`. """
776 if self.store and not (self.depends and env.in_draft):
777 # this is a stored field
779 # this is a stored computed field, check for recomputation
780 recs = record._recompute_check(self)
782 # recompute the value (only in cache)
783 self.compute_value(recs)
784 # HACK: if result is in the wrong cache, copy values
786 for source, target in zip(recs, recs.with_env(env)):
788 values = target._convert_to_cache({
789 f.name: source[f.name] for f in self.computed_fields
791 except MissingError as e:
792 values = FailedValue(e)
793 target._cache.update(values)
794 # the result is saved to database by BaseModel.recompute()
797 # read the field from database
798 record._prefetch_field(self)
801 # this is either a non-stored computed field, or a stored computed
802 # field in draft mode
804 self.compute_value(record)
806 recs = record._in_cache_without(self)
807 self.compute_value(recs)
810 # this is a non-stored non-computed field
811 record._cache[self] = self.null(env)
813 def determine_default(self, record):
814 """ determine the default value of field `self` on `record` """
816 self._compute_value(record)
818 record._cache[self] = SpecialValue(self.null(record.env))
820 def determine_inverse(self, records):
821 """ Given the value of `self` on `records`, inverse the computation. """
823 self.inverse(records)
825 def determine_domain(self, records, operator, value):
826 """ Return a domain representing a condition on `self`. """
828 return self.search(records, operator, value)
830 return [(self.name, operator, value)]
832 ############################################################################
834 # Notification when fields are modified
837 def modified(self, records):
838 """ Notify that field `self` has been modified on `records`: prepare the
839 fields/records to recompute, and return a spec indicating what to
842 # invalidate the fields that depend on self, and prepare recomputation
843 spec = [(self, records._ids)]
844 for field, path in self._triggers:
845 if path and field.store:
846 # don't move this line to function top, see log
847 env = records.env(user=SUPERUSER_ID, context={'active_test': False})
848 target = env[field.model_name].search([(path, 'in', records.ids)])
850 spec.append((field, target._ids))
851 target.with_env(records.env)._recompute_todo(field)
853 spec.append((field, None))
857 def modified_draft(self, records):
858 """ Same as :meth:`modified`, but in draft mode. """
861 # invalidate the fields on the records in cache that depend on
862 # `records`, except fields currently being computed
864 for field, path in self._triggers:
865 target = env[field.model_name]
866 computed = target.browse(env.computed[field])
868 target = records - computed
870 target = (target.browse(env.cache[field]) - computed).filtered(
871 lambda rec: rec._mapped_cache(path) & records
874 target = target.browse(env.cache[field]) - computed
877 spec.append((field, target._ids))
882 class Boolean(Field):
885 def convert_to_cache(self, value, record, validate=True):
888 def convert_to_export(self, value, env):
889 if env.context.get('export_raw_data'):
894 class Integer(Field):
897 def convert_to_cache(self, value, record, validate=True):
898 return int(value or 0)
900 def convert_to_read(self, value, use_name_get=True):
901 # Integer values greater than 2^31-1 are not supported in pure XMLRPC,
902 # so we have to pass them as floats :-(
903 if value and value > xmlrpclib.MAXINT:
907 def _update(self, records, value):
908 # special case, when an integer field is used as inverse for a one2many
909 records._cache[self] = value.id or 0
913 """ The precision digits are given by the attribute
915 :param digits: a pair (total, decimal), or a function taking a database
916 cursor and returning a pair (total, decimal)
919 _digits = None # digits argument passed to class initializer
920 digits = None # digits as computed by setup()
922 def __init__(self, string=None, digits=None, **kwargs):
923 super(Float, self).__init__(string=string, _digits=digits, **kwargs)
925 def _setup_regular(self, env):
926 super(Float, self)._setup_regular(env)
927 self.digits = self._digits(env.cr) if callable(self._digits) else self._digits
929 _related_digits = property(attrgetter('digits'))
931 _description_digits = property(attrgetter('digits'))
933 _column_digits = property(lambda self: not callable(self._digits) and self._digits)
934 _column_digits_compute = property(lambda self: callable(self._digits) and self._digits)
936 def convert_to_cache(self, value, record, validate=True):
937 # apply rounding here, otherwise value in cache may be wrong!
939 return float_round(float(value or 0.0), precision_digits=self.digits[1])
941 return float(value or 0.0)
944 class _String(Field):
945 """ Abstract class for string fields. """
948 _column_translate = property(attrgetter('translate'))
949 _related_translate = property(attrgetter('translate'))
950 _description_translate = property(attrgetter('translate'))
954 """ Basic string field, can be length-limited, usually displayed as a
955 single-line string in clients
957 :param int size: the maximum size of values stored for that field
958 :param bool translate: whether the values of this field can be translated
963 _column_size = property(attrgetter('size'))
964 _related_size = property(attrgetter('size'))
965 _description_size = property(attrgetter('size'))
967 def convert_to_cache(self, value, record, validate=True):
968 if value is None or value is False:
970 return ustr(value)[:self.size]
973 """ Text field. Very similar to :class:`~.Char` but used for longer
974 contents and displayed as a multiline text box
976 :param translate: whether the value of this field can be translated
980 def convert_to_cache(self, value, record, validate=True):
981 if value is None or value is False:
987 sanitize = True # whether value must be sanitized
989 _column_sanitize = property(attrgetter('sanitize'))
990 _related_sanitize = property(attrgetter('sanitize'))
991 _description_sanitize = property(attrgetter('sanitize'))
993 def convert_to_cache(self, value, record, validate=True):
994 if value is None or value is False:
996 if validate and self.sanitize:
997 return html_sanitize(value)
1006 """ Return the current day in the format expected by the ORM.
1007 This function may be used to compute default values.
1009 return date.today().strftime(DATE_FORMAT)
1012 def context_today(record, timestamp=None):
1013 """ Return the current date as seen in the client's timezone in a format
1014 fit for date fields. This method may be used to compute default
1017 :param datetime timestamp: optional datetime value to use instead of
1018 the current date and time (must be a datetime, regular dates
1019 can't be converted between timezones.)
1022 today = timestamp or datetime.now()
1023 context_today = None
1024 tz_name = record._context.get('tz') or record.env.user.tz
1027 today_utc = pytz.timezone('UTC').localize(today, is_dst=False) # UTC = no DST
1028 context_today = today_utc.astimezone(pytz.timezone(tz_name))
1030 _logger.debug("failed to compute context/client-specific today date, using UTC value for `today`",
1032 return (context_today or today).strftime(DATE_FORMAT)
1035 def from_string(value):
1036 """ Convert an ORM `value` into a :class:`date` value. """
1037 value = value[:DATE_LENGTH]
1038 return datetime.strptime(value, DATE_FORMAT).date()
1041 def to_string(value):
1042 """ Convert a :class:`date` value into the format expected by the ORM. """
1043 return value.strftime(DATE_FORMAT)
1045 def convert_to_cache(self, value, record, validate=True):
1048 if isinstance(value, basestring):
1050 # force parsing for validation
1051 self.from_string(value)
1052 return value[:DATE_LENGTH]
1053 return self.to_string(value)
1055 def convert_to_export(self, value, env):
1056 if value and env.context.get('export_raw_data'):
1057 return self.from_string(value)
1058 return bool(value) and ustr(value)
1061 class Datetime(Field):
1066 """ Return the current day and time in the format expected by the ORM.
1067 This function may be used to compute default values.
1069 return datetime.now().strftime(DATETIME_FORMAT)
1072 def context_timestamp(record, timestamp):
1073 """Returns the given timestamp converted to the client's timezone.
1074 This method is *not* meant for use as a _defaults initializer,
1075 because datetime fields are automatically converted upon
1076 display on client side. For _defaults you :meth:`fields.datetime.now`
1077 should be used instead.
1079 :param datetime timestamp: naive datetime value (expressed in UTC)
1080 to be converted to the client timezone
1082 :return: timestamp converted to timezone-aware datetime in context
1085 assert isinstance(timestamp, datetime), 'Datetime instance expected'
1086 tz_name = record._context.get('tz') or record.env.user.tz
1089 utc = pytz.timezone('UTC')
1090 context_tz = pytz.timezone(tz_name)
1091 utc_timestamp = utc.localize(timestamp, is_dst=False) # UTC = no DST
1092 return utc_timestamp.astimezone(context_tz)
1094 _logger.debug("failed to compute context/client-specific timestamp, "
1095 "using the UTC value",
1100 def from_string(value):
1101 """ Convert an ORM `value` into a :class:`datetime` value. """
1102 value = value[:DATETIME_LENGTH]
1103 if len(value) == DATE_LENGTH:
1104 value += " 00:00:00"
1105 return datetime.strptime(value, DATETIME_FORMAT)
1108 def to_string(value):
1109 """ Convert a :class:`datetime` value into the format expected by the ORM. """
1110 return value.strftime(DATETIME_FORMAT)
1112 def convert_to_cache(self, value, record, validate=True):
1115 if isinstance(value, basestring):
1117 # force parsing for validation
1118 self.from_string(value)
1119 value = value[:DATETIME_LENGTH]
1120 if len(value) == DATE_LENGTH:
1121 value += " 00:00:00"
1123 return self.to_string(value)
1125 def convert_to_export(self, value, env):
1126 if value and env.context.get('export_raw_data'):
1127 return self.from_string(value)
1128 return bool(value) and ustr(value)
1131 class Binary(Field):
1135 class Selection(Field):
1137 :param selection: specifies the possible values for this field.
1138 It is given as either a list of pairs (`value`, `string`), or a
1139 model method, or a method name.
1140 :param selection_add: provides an extension of the selection in the case
1141 of an overridden field. It is a list of pairs (`value`, `string`).
1143 The attribute `selection` is mandatory except in the case of
1144 :ref:`related fields <field-related>` or :ref:`field extensions
1145 <field-incremental-definition>`.
1148 selection = None # [(value, string), ...], function or method name
1149 selection_add = None # [(value, string), ...]
1151 def __init__(self, selection=None, string=None, **kwargs):
1152 if callable(selection):
1153 from openerp import api
1154 selection = api.expected(api.model, selection)
1155 super(Selection, self).__init__(selection=selection, string=string, **kwargs)
1157 def _setup_related(self, env):
1158 super(Selection, self)._setup_related(env)
1159 # selection must be computed on related field
1160 field = self.related_field
1161 self.selection = lambda model: field._description_selection(model.env)
1163 def _setup_regular(self, env):
1164 super(Selection, self)._setup_regular(env)
1165 # determine selection (applying extensions)
1166 cls = type(env[self.model_name])
1168 for field in resolve_all_mro(cls, self.name, reverse=True):
1169 if isinstance(field, type(self)):
1170 # We cannot use field.selection or field.selection_add here
1171 # because those attributes are overridden by `set_class_name`.
1172 if 'selection' in field._attrs:
1173 selection = field._attrs['selection']
1174 if 'selection_add' in field._attrs:
1175 selection = selection + field._attrs['selection_add']
1178 self.selection = selection
1180 def _description_selection(self, env):
1181 """ return the selection list (pairs (value, label)); labels are
1182 translated according to context language
1184 selection = self.selection
1185 if isinstance(selection, basestring):
1186 return getattr(env[self.model_name], selection)()
1187 if callable(selection):
1188 return selection(env[self.model_name])
1190 # translate selection labels
1192 name = "%s,%s" % (self.model_name, self.name)
1193 translate = partial(
1194 env['ir.translation']._get_source, name, 'selection', env.lang)
1195 return [(value, translate(label)) for value, label in selection]
1200 def _column_selection(self):
1201 if isinstance(self.selection, basestring):
1202 method = self.selection
1203 return lambda self, *a, **kw: getattr(self, method)(*a, **kw)
1205 return self.selection
1207 def get_values(self, env):
1208 """ return a list of the possible values """
1209 selection = self.selection
1210 if isinstance(selection, basestring):
1211 selection = getattr(env[self.model_name], selection)()
1212 elif callable(selection):
1213 selection = selection(env[self.model_name])
1214 return [value for value, _ in selection]
1216 def convert_to_cache(self, value, record, validate=True):
1218 return value or False
1219 if value in self.get_values(record.env):
1223 raise ValueError("Wrong value for %s: %r" % (self, value))
1225 def convert_to_export(self, value, env):
1226 if not isinstance(self.selection, list):
1227 # FIXME: this reproduces an existing buggy behavior!
1229 for item in self._description_selection(env):
1230 if item[0] == value:
1235 class Reference(Selection):
1239 def __init__(self, selection=None, string=None, **kwargs):
1240 super(Reference, self).__init__(selection=selection, string=string, **kwargs)
1242 _related_size = property(attrgetter('size'))
1244 _column_size = property(attrgetter('size'))
1246 def convert_to_cache(self, value, record, validate=True):
1247 if isinstance(value, BaseModel):
1248 if ((not validate or value._name in self.get_values(record.env))
1249 and len(value) <= 1):
1250 return value.with_env(record.env) or False
1251 elif isinstance(value, basestring):
1252 res_model, res_id = value.split(',')
1253 return record.env[res_model].browse(int(res_id))
1256 raise ValueError("Wrong value for %s: %r" % (self, value))
1258 def convert_to_read(self, value, use_name_get=True):
1259 return "%s,%s" % (value._name, value.id) if value else False
1261 def convert_to_export(self, value, env):
1262 return bool(value) and value.name_get()[0][1]
1264 def convert_to_display_name(self, value):
1265 return ustr(value and value.display_name)
1268 class _Relational(Field):
1269 """ Abstract class for relational fields. """
1271 domain = None # domain for searching values
1272 context = None # context for searching values
1274 _description_relation = property(attrgetter('comodel_name'))
1275 _description_context = property(attrgetter('context'))
1277 def _description_domain(self, env):
1278 return self.domain(env[self.model_name]) if callable(self.domain) else self.domain
1280 _column_obj = property(attrgetter('comodel_name'))
1281 _column_domain = property(attrgetter('domain'))
1282 _column_context = property(attrgetter('context'))
1284 def null(self, env):
1285 return env[self.comodel_name]
1287 def modified(self, records):
1288 # Invalidate cache for self.inverse_fields, too. Note that recomputation
1289 # of fields that depend on self.inverse_fields is already covered by the
1290 # triggers (see above).
1291 spec = super(_Relational, self).modified(records)
1292 for invf in self.inverse_fields:
1293 spec.append((invf, None))
1297 class Many2one(_Relational):
1298 """ The value of such a field is a recordset of size 0 (no
1299 record) or 1 (a single record).
1301 :param comodel_name: name of the target model (string)
1303 :param domain: an optional domain to set on candidate values on the
1304 client side (domain or string)
1306 :param context: an optional context to use on the client side when
1307 handling that field (dictionary)
1309 :param ondelete: what to do when the referred record is deleted;
1310 possible values are: ``'set null'``, ``'restrict'``, ``'cascade'``
1312 :param auto_join: whether JOINs are generated upon search through that
1313 field (boolean, by default ``False``)
1315 :param delegate: set it to ``True`` to make fields of the target model
1316 accessible from the current model (corresponds to ``_inherits``)
1318 The attribute `comodel_name` is mandatory except in the case of related
1319 fields or field extensions.
1322 ondelete = 'set null' # what to do when value is deleted
1323 auto_join = False # whether joins are generated upon search
1324 delegate = False # whether self implements delegation
1326 def __init__(self, comodel_name=None, string=None, **kwargs):
1327 super(Many2one, self).__init__(comodel_name=comodel_name, string=string, **kwargs)
1329 def _setup_regular(self, env):
1330 super(Many2one, self)._setup_regular(env)
1332 # self.inverse_fields is populated by the corresponding One2many field
1334 # determine self.delegate
1335 self.delegate = self.name in env[self.model_name]._inherits.values()
1337 _column_ondelete = property(attrgetter('ondelete'))
1338 _column_auto_join = property(attrgetter('auto_join'))
1340 def _update(self, records, value):
1341 """ Update the cached value of `self` for `records` with `value`. """
1342 records._cache[self] = value
1344 def convert_to_cache(self, value, record, validate=True):
1345 if isinstance(value, (NoneType, int)):
1346 return record.env[self.comodel_name].browse(value)
1347 if isinstance(value, BaseModel):
1348 if value._name == self.comodel_name and len(value) <= 1:
1349 return value.with_env(record.env)
1350 raise ValueError("Wrong value for %s: %r" % (self, value))
1351 elif isinstance(value, tuple):
1352 return record.env[self.comodel_name].browse(value[0])
1353 elif isinstance(value, dict):
1354 return record.env[self.comodel_name].new(value)
1356 return record.env[self.comodel_name].browse(value)
1358 def convert_to_read(self, value, use_name_get=True):
1359 if use_name_get and value:
1360 # evaluate name_get() as superuser, because the visibility of a
1361 # many2one field value (id and name) depends on the current record's
1362 # access rights, and not the value's access rights.
1363 return value.sudo().name_get()[0]
1367 def convert_to_write(self, value, target=None, fnames=None):
1370 def convert_to_onchange(self, value):
1373 def convert_to_export(self, value, env):
1374 return bool(value) and value.name_get()[0][1]
1376 def convert_to_display_name(self, value):
1377 return ustr(value.display_name)
1379 def determine_default(self, record):
1380 super(Many2one, self).determine_default(record)
1382 # special case: fields that implement inheritance between models
1383 value = record[self.name]
1385 # the default value cannot be null, use a new record instead
1386 record[self.name] = record.env[self.comodel_name].new()
1389 class UnionUpdate(SpecialValue):
1390 """ Placeholder for a value update; when this value is taken from the cache,
1391 it returns ``record[field.name] | value`` and stores it in the cache.
1393 def __init__(self, field, record, value):
1394 self.args = (field, record, value)
1397 field, record, value = self.args
1398 # in order to read the current field's value, remove self from cache
1399 del record._cache[field]
1400 # read the current field's value, and update it in cache only
1401 record._cache[field] = new_value = record[field.name] | value
1405 class _RelationalMulti(_Relational):
1406 """ Abstract class for relational fields *2many. """
1408 def _update(self, records, value):
1409 """ Update the cached value of `self` for `records` with `value`. """
1410 for record in records:
1411 if self in record._cache:
1412 record._cache[self] = record[self.name] | value
1414 record._cache[self] = UnionUpdate(self, record, value)
1416 def convert_to_cache(self, value, record, validate=True):
1417 if isinstance(value, BaseModel):
1418 if value._name == self.comodel_name:
1419 return value.with_env(record.env)
1420 elif isinstance(value, list):
1421 # value is a list of record ids or commands
1423 record = record.browse() # new record has no value
1424 result = record[self.name]
1425 # modify result with the commands;
1426 # beware to not introduce duplicates in result
1427 for command in value:
1428 if isinstance(command, (tuple, list)):
1430 result += result.new(command[2])
1431 elif command[0] == 1:
1432 result.browse(command[1]).update(command[2])
1433 elif command[0] == 2:
1434 # note: the record will be deleted by write()
1435 result -= result.browse(command[1])
1436 elif command[0] == 3:
1437 result -= result.browse(command[1])
1438 elif command[0] == 4:
1439 result += result.browse(command[1]) - result
1440 elif command[0] == 5:
1441 result = result.browse()
1442 elif command[0] == 6:
1443 result = result.browse(command[2])
1444 elif isinstance(command, dict):
1445 result += result.new(command)
1447 result += result.browse(command) - result
1450 return self.null(record.env)
1451 raise ValueError("Wrong value for %s: %s" % (self, value))
1453 def convert_to_read(self, value, use_name_get=True):
1456 def convert_to_write(self, value, target=None, fnames=None):
1457 # remove/delete former records
1460 result = [(6, 0, set_ids)]
1461 add_existing = lambda id: set_ids.append(id)
1463 tag = 2 if self.type == 'one2many' else 3
1464 result = [(tag, record.id) for record in target[self.name] - value]
1465 add_existing = lambda id: result.append((4, id))
1468 # take all fields in cache, except the inverses of self
1469 fnames = set(value._fields) - set(MAGIC_COLUMNS)
1470 for invf in self.inverse_fields:
1471 fnames.discard(invf.name)
1473 # add new and existing records
1474 for record in value:
1475 if not record.id or record._dirty:
1476 values = dict((k, v) for k, v in record._cache.iteritems() if k in fnames)
1477 values = record._convert_to_write(values)
1479 result.append((0, 0, values))
1481 result.append((1, record.id, values))
1483 add_existing(record.id)
1487 def convert_to_export(self, value, env):
1488 return bool(value) and ','.join(name for id, name in value.name_get())
1490 def convert_to_display_name(self, value):
1491 raise NotImplementedError()
1493 def _compute_related(self, records):
1494 """ Compute the related field `self` on `records`. """
1495 for record in records:
1497 # traverse the intermediate fields, and keep at most one record
1498 for name in self.related[:-1]:
1499 value = value[name][:1]
1500 record[self.name] = value[self.related[-1]]
1503 class One2many(_RelationalMulti):
1504 """ One2many field; the value of such a field is the recordset of all the
1505 records in `comodel_name` such that the field `inverse_name` is equal to
1508 :param comodel_name: name of the target model (string)
1510 :param inverse_name: name of the inverse `Many2one` field in
1511 `comodel_name` (string)
1513 :param domain: an optional domain to set on candidate values on the
1514 client side (domain or string)
1516 :param context: an optional context to use on the client side when
1517 handling that field (dictionary)
1519 :param auto_join: whether JOINs are generated upon search through that
1520 field (boolean, by default ``False``)
1522 :param limit: optional limit to use upon read (integer)
1524 The attributes `comodel_name` and `inverse_name` are mandatory except in
1525 the case of related fields or field extensions.
1528 inverse_name = None # name of the inverse field
1529 auto_join = False # whether joins are generated upon search
1530 limit = None # optional limit to use upon read
1531 copyable = False # o2m are not copied by default
1533 def __init__(self, comodel_name=None, inverse_name=None, string=None, **kwargs):
1534 super(One2many, self).__init__(
1535 comodel_name=comodel_name,
1536 inverse_name=inverse_name,
1541 def _setup_regular(self, env):
1542 super(One2many, self)._setup_regular(env)
1544 if self.inverse_name:
1545 # link self to its inverse field and vice-versa
1546 invf = env[self.comodel_name]._fields[self.inverse_name]
1547 self.inverse_fields.append(invf)
1548 invf.inverse_fields.append(self)
1550 _description_relation_field = property(attrgetter('inverse_name'))
1552 _column_fields_id = property(attrgetter('inverse_name'))
1553 _column_auto_join = property(attrgetter('auto_join'))
1554 _column_limit = property(attrgetter('limit'))
1557 class Many2many(_RelationalMulti):
1558 """ Many2many field; the value of such a field is the recordset.
1560 :param comodel_name: name of the target model (string)
1562 The attribute `comodel_name` is mandatory except in the case of related
1563 fields or field extensions.
1565 :param relation: optional name of the table that stores the relation in
1566 the database (string)
1568 :param column1: optional name of the column referring to "these" records
1569 in the table `relation` (string)
1571 :param column2: optional name of the column referring to "those" records
1572 in the table `relation` (string)
1574 The attributes `relation`, `column1` and `column2` are optional. If not
1575 given, names are automatically generated from model names, provided
1576 `model_name` and `comodel_name` are different!
1578 :param domain: an optional domain to set on candidate values on the
1579 client side (domain or string)
1581 :param context: an optional context to use on the client side when
1582 handling that field (dictionary)
1584 :param limit: optional limit to use upon read (integer)
1588 relation = None # name of table
1589 column1 = None # column of table referring to model
1590 column2 = None # column of table referring to comodel
1591 limit = None # optional limit to use upon read
1593 def __init__(self, comodel_name=None, relation=None, column1=None, column2=None,
1594 string=None, **kwargs):
1595 super(Many2many, self).__init__(
1596 comodel_name=comodel_name,
1604 def _setup_regular(self, env):
1605 super(Many2many, self)._setup_regular(env)
1607 if self.store and not self.relation:
1608 model = env[self.model_name]
1609 column = model._columns[self.name]
1610 if not isinstance(column, fields.function):
1611 self.relation, self.column1, self.column2 = column._sql_names(model)
1614 m2m = env.registry._m2m
1615 # if inverse field has already been setup, it is present in m2m
1616 invf = m2m.get((self.relation, self.column2, self.column1))
1618 self.inverse_fields.append(invf)
1619 invf.inverse_fields.append(self)
1621 # add self in m2m, so that its inverse field can find it
1622 m2m[(self.relation, self.column1, self.column2)] = self
1624 _column_rel = property(attrgetter('relation'))
1625 _column_id1 = property(attrgetter('column1'))
1626 _column_id2 = property(attrgetter('column2'))
1627 _column_limit = property(attrgetter('limit'))
1631 """ Special case for field 'id'. """
1633 #: Can't write this!
1636 def __init__(self, string=None, **kwargs):
1637 super(Id, self).__init__(type='integer', string=string, **kwargs)
1639 def to_column(self):
1640 """ to_column() -> fields._column
1644 return fields.integer('ID')
1646 def __get__(self, record, owner):
1648 return self # the field is accessed through the class owner
1651 return record.ensure_one()._ids[0]
1653 def __set__(self, record, value):
1654 raise TypeError("field 'id' cannot be assigned")
1657 # imported here to avoid dependency cycle issues
1658 from openerp import SUPERUSER_ID
1659 from .exceptions import Warning, MissingError
1660 from .models import BaseModel, MAGIC_COLUMNS
1661 from .osv import fields