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__)
44 class SpecialValue(object):
45 """ Encapsulates a value in the cache in place of a normal value. """
46 def __init__(self, value):
51 class FailedValue(SpecialValue):
52 """ Special value that encapsulates an exception instead of a value. """
53 def __init__(self, exception):
54 self.exception = exception
58 def _check_value(value):
59 """ Return `value`, or call its getter if `value` is a :class:`SpecialValue`. """
60 return value.get() if isinstance(value, SpecialValue) else value
63 def resolve_all_mro(cls, name, reverse=False):
64 """ Return the (successively overridden) values of attribute `name` in `cls`
65 in mro order, or inverse mro order if `reverse` is true.
67 klasses = reversed(cls.__mro__) if reverse else cls.__mro__
69 if name in klass.__dict__:
70 yield klass.__dict__[name]
73 def default_compute(field, value):
74 """ Return a compute function for the given default `value`; `value` is
75 either a constant, or a unary function returning the default value.
78 func = value if callable(value) else lambda rec: value
85 class MetaField(type):
86 """ Metaclass for field classes. """
89 def __init__(cls, name, bases, attrs):
90 super(MetaField, cls).__init__(name, bases, attrs)
92 cls.by_type[cls.type] = cls
94 # compute class attributes to avoid calling dir() on fields
96 cls.related_attrs = []
97 cls.description_attrs = []
99 if attr.startswith('_column_'):
100 cls.column_attrs.append((attr[8:], attr))
101 elif attr.startswith('_related_'):
102 cls.related_attrs.append((attr[9:], attr))
103 elif attr.startswith('_description_'):
104 cls.description_attrs.append((attr[13:], attr))
108 """ The field descriptor contains the field definition, and manages accesses
109 and assignments of the corresponding field on records. The following
110 attributes may be provided when instanciating a field:
112 :param string: the label of the field seen by users (string); if not
113 set, the ORM takes the field name in the class (capitalized).
115 :param help: the tooltip of the field seen by users (string)
117 :param readonly: whether the field is readonly (boolean, by default ``False``)
119 :param required: whether the value of the field is required (boolean, by
122 :param index: whether the field is indexed in database (boolean, by
125 :param default: the default value for the field; this is either a static
126 value, or a function taking a recordset and returning a value
128 :param states: a dictionary mapping state values to lists of UI attribute-value
129 pairs; possible attributes are: 'readonly', 'required', 'invisible'.
130 Note: Any state-based condition requires the ``state`` field value to be
131 available on the client-side UI. This is typically done by including it in
132 the relevant views, possibly made invisible if not relevant for the
135 :param groups: comma-separated list of group xml ids (string); this
136 restricts the field access to the users of the given groups only
138 :param bool copy: whether the field value should be copied when the record
139 is duplicated (default: ``True`` for normal fields, ``False`` for
140 ``one2many`` and computed fields, including property fields and
145 .. rubric:: Computed fields
147 One can define a field whose value is computed instead of simply being
148 read from the database. The attributes that are specific to computed
149 fields are given below. To define such a field, simply provide a value
150 for the attribute `compute`.
152 :param compute: name of a method that computes the field
154 :param inverse: name of a method that inverses the field (optional)
156 :param search: name of a method that implement search on the field (optional)
158 :param store: whether the field is stored in database (boolean, by
159 default ``False`` on computed fields)
161 The methods given for `compute`, `inverse` and `search` are model
162 methods. Their signature is shown in the following example::
164 upper = fields.Char(compute='_compute_upper',
165 inverse='_inverse_upper',
166 search='_search_upper')
169 def _compute_upper(self):
171 self.upper = self.name.upper() if self.name else False
173 def _inverse_upper(self):
175 self.name = self.upper.lower() if self.upper else False
177 def _search_upper(self, operator, value):
178 if operator == 'like':
180 return [('name', operator, value)]
182 The compute method has to assign the field on all records of the invoked
183 recordset. The decorator :meth:`openerp.api.depends` must be applied on
184 the compute method to specify the field dependencies; those dependencies
185 are used to determine when to recompute the field; recomputation is
186 automatic and guarantees cache/database consistency. Note that the same
187 method can be used for several fields, you simply have to assign all the
188 given fields in the method; the method will be invoked once for all
191 By default, a computed field is not stored to the database, and is
192 computed on-the-fly. Adding the attribute ``store=True`` will store the
193 field's values in the database. The advantage of a stored field is that
194 searching on that field is done by the database itself. The disadvantage
195 is that it requires database updates when the field must be recomputed.
197 The inverse method, as its name says, does the inverse of the compute
198 method: the invoked records have a value for the field, and you must
199 apply the necessary changes on the field dependencies such that the
200 computation gives the expected value. Note that a computed field without
201 an inverse method is readonly by default.
203 The search method is invoked when processing domains before doing an
204 actual search on the model. It must return a domain equivalent to the
205 condition: `field operator value`.
209 .. rubric:: Related fields
211 The value of a related field is given by following a sequence of
212 relational fields and reading a field on the reached model. The complete
213 sequence of fields to traverse is specified by the attribute
215 :param related: sequence of field names
217 The value of some attributes from related fields are automatically taken
218 from the source field, when it makes sense. Examples are the attributes
219 `string` or `selection` on selection fields.
221 By default, the values of related fields are not stored to the database.
222 Add the attribute ``store=True`` to make it stored, just like computed
223 fields. Related fields are automatically recomputed when their
224 dependencies are modified.
226 .. _field-company-dependent:
228 .. rubric:: Company-dependent fields
230 Formerly known as 'property' fields, the value of those fields depends
231 on the company. In other words, users that belong to different companies
232 may see different values for the field on a given record.
234 :param company_dependent: whether the field is company-dependent (boolean)
236 .. _field-incremental-definition:
238 .. rubric:: Incremental definition
240 A field is defined as class attribute on a model class. If the model is
241 extended (see :class:`BaseModel`), one can also extend the field
242 definition by redefining a field with the same name and same type on the
243 subclass. In that case, the attributes of the field are taken from the
244 parent class and overridden by the ones given in subclasses.
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 """ make a copy of `self`, possibly modified with parameters `kwargs` """
303 field._attrs = {key: val for key, val in kwargs.iteritems() if val is not None}
304 field._free_attrs = list(self._free_attrs)
307 def set_class_name(self, cls, name):
308 """ Assign the model class and field name of `self`. """
309 self.model_name = cls._name
312 # determine all inherited field attributes
314 for field in resolve_all_mro(cls, name, reverse=True):
315 if isinstance(field, type(self)):
316 attrs.update(field._attrs)
319 attrs.update(self._attrs) # necessary in case self is not in cls
321 # initialize `self` with `attrs`
322 if attrs.get('compute'):
323 # by default, computed fields are not stored, not copied and readonly
324 attrs['store'] = attrs.get('store', False)
325 attrs['copy'] = attrs.get('copy', False)
326 attrs['readonly'] = attrs.get('readonly', not attrs.get('inverse'))
327 if attrs.get('related'):
328 # by default, related fields are not stored
329 attrs['store'] = attrs.get('store', False)
331 # attribute is copyable because there is also a copy() method
332 attrs['copyable'] = attrs.pop('copy')
334 for attr, value in attrs.iteritems():
335 if not hasattr(self, attr):
336 self._free_attrs.append(attr)
337 setattr(self, attr, value)
340 self.string = name.replace('_', ' ').capitalize()
345 return "%s.%s" % (self.model_name, self.name)
348 return "%s.%s" % (self.model_name, self.name)
350 ############################################################################
356 """ Prepare `self` for a new setup. """
357 self._setup_done = False
358 # self._triggers is a set of pairs (field, path) that represents the
359 # computed fields that depend on `self`. When `self` is modified, it
360 # invalidates the cache of each `field`, and registers the records to
361 # recompute based on `path`. See method `modified` below for details.
362 self._triggers = set()
363 self.inverse_fields = []
365 def setup(self, env):
366 """ Complete the setup of `self` (dependencies, recomputation triggers,
367 and other properties). This method is idempotent: it has no effect
368 if `self` has already been set up.
370 if not self._setup_done:
371 self._setup_done = True
374 def _setup(self, env):
375 """ Do the actual setup of `self`. """
377 self._setup_related(env)
379 self._setup_regular(env)
381 # put invalidation/recomputation triggers on field dependencies
382 model = env[self.model_name]
383 for path in self.depends:
384 self._setup_dependency([], model, path.split('.'))
386 # put invalidation triggers on model dependencies
387 for dep_model_name, field_names in model._depends.iteritems():
388 dep_model = env[dep_model_name]
389 for field_name in field_names:
390 field = dep_model._fields[field_name]
391 field._triggers.add((self, None))
394 # Setup of related fields
397 def _setup_related(self, env):
398 """ Setup the attributes of a related field. """
399 # fix the type of self.related if necessary
400 if isinstance(self.related, basestring):
401 self.related = tuple(self.related.split('.'))
403 # determine the related field, and make sure it is set up
404 recs = env[self.model_name]
405 for name in self.related[:-1]:
407 field = self.related_field = recs._fields[self.related[-1]]
410 # check type consistency
411 if self.type != field.type:
412 raise Warning("Type of related field %s is inconsistent with %s" % (self, field))
414 # determine dependencies, compute, inverse, and search
415 self.depends = ('.'.join(self.related),)
416 self.compute = self._compute_related
417 self.inverse = self._inverse_related
418 if field._description_searchable(env):
419 self.search = self._search_related
421 # copy attributes from field to self (string, help, etc.)
422 for attr, prop in self.related_attrs:
423 if not getattr(self, attr):
424 setattr(self, attr, getattr(field, prop))
426 def _compute_related(self, records):
427 """ Compute the related field `self` on `records`. """
428 for record, sudo_record in zip(records, records.sudo()):
429 # bypass access rights check when traversing the related path
430 value = sudo_record if record.id else record
431 # traverse the intermediate fields, and keep at most one record
432 for name in self.related[:-1]:
433 value = value[name][:1]
434 record[self.name] = value[self.related[-1]]
436 def _inverse_related(self, records):
437 """ Inverse the related field `self` on `records`. """
438 for record in records:
440 # traverse the intermediate fields, and keep at most one record
441 for name in self.related[:-1]:
442 other = other[name][:1]
444 other[self.related[-1]] = record[self.name]
446 def _search_related(self, records, operator, value):
447 """ Determine the domain to search on field `self`. """
448 return [('.'.join(self.related), operator, value)]
450 # properties used by _setup_related() to copy values from related field
451 _related_string = property(attrgetter('string'))
452 _related_help = property(attrgetter('help'))
453 _related_readonly = property(attrgetter('readonly'))
454 _related_groups = property(attrgetter('groups'))
457 # Setup of non-related fields
460 def _setup_regular(self, env):
461 """ Setup the attributes of a non-related field. """
462 recs = env[self.model_name]
464 def make_depends(deps):
465 return tuple(deps(recs) if callable(deps) else deps)
467 # transform self.default into self.compute
468 if self.default is not None and self.compute is None:
469 self.compute = default_compute(self, self.default)
471 # convert compute into a callable and determine depends
472 if isinstance(self.compute, basestring):
473 # if the compute method has been overridden, concatenate all their _depends
475 for method in resolve_all_mro(type(recs), self.compute, reverse=True):
476 self.depends += make_depends(getattr(method, '_depends', ()))
477 self.compute = getattr(type(recs), self.compute)
479 self.depends = make_depends(getattr(self.compute, '_depends', ()))
481 # convert inverse and search into callables
482 if isinstance(self.inverse, basestring):
483 self.inverse = getattr(type(recs), self.inverse)
484 if isinstance(self.search, basestring):
485 self.search = getattr(type(recs), self.search)
487 def _setup_dependency(self, path0, model, path1):
488 """ Make `self` depend on `model`; `path0 + path1` is a dependency of
489 `self`, and `path0` is the sequence of field names from `self.model`
493 head, tail = path1[0], path1[1:]
496 # special case: add triggers on all fields of model (except self)
497 fields = set(model._fields.itervalues()) - set([self])
499 fields = [model._fields[head]]
503 _logger.debug("Field %s is recursively defined", self)
504 self.recursive = True
509 #_logger.debug("Add trigger on %s to recompute %s", field, self)
510 field._triggers.add((self, '.'.join(path0 or ['id'])))
512 # add trigger on inverse fields, too
513 for invf in field.inverse_fields:
514 #_logger.debug("Add trigger on %s to recompute %s", invf, self)
515 invf._triggers.add((self, '.'.join(path0 + [head])))
517 # recursively traverse the dependency
519 comodel = env[field.comodel_name]
520 self._setup_dependency(path0 + [head], comodel, tail)
523 def dependents(self):
524 """ Return the computed fields that depend on `self`. """
525 return (field for field, path in self._triggers)
527 ############################################################################
532 def get_description(self, env):
533 """ Return a dictionary that describes the field `self`. """
534 desc = {'type': self.type}
535 for attr, prop in self.description_attrs:
536 value = getattr(self, prop)
539 if value is not None:
544 # properties used by get_description()
546 def _description_store(self, env):
548 # if the corresponding column is a function field, check the column
549 column = env[self.model_name]._columns.get(self.name)
550 return bool(getattr(column, 'store', True))
553 def _description_searchable(self, env):
554 return self._description_store(env) or bool(self.search)
556 _description_depends = property(attrgetter('depends'))
557 _description_related = property(attrgetter('related'))
558 _description_company_dependent = property(attrgetter('company_dependent'))
559 _description_readonly = property(attrgetter('readonly'))
560 _description_required = property(attrgetter('required'))
561 _description_states = property(attrgetter('states'))
562 _description_groups = property(attrgetter('groups'))
563 _description_change_default = property(attrgetter('change_default'))
564 _description_deprecated = property(attrgetter('deprecated'))
566 def _description_string(self, env):
567 if self.string and env.lang:
568 name = "%s,%s" % (self.model_name, self.name)
569 trans = env['ir.translation']._get_source(name, 'field', env.lang)
570 return trans or self.string
573 def _description_help(self, env):
574 if self.help and env.lang:
575 name = "%s,%s" % (self.model_name, self.name)
576 trans = env['ir.translation']._get_source(name, 'help', env.lang)
577 return trans or self.help
580 ############################################################################
582 # Conversion to column instance
586 """ return a low-level field object corresponding to `self` """
589 assert isinstance(self._origin, fields._column)
592 _logger.debug("Create fields._column for Field %s", self)
594 for attr, prop in self.column_attrs:
595 args[attr] = getattr(self, prop)
596 for attr in self._free_attrs:
597 args[attr] = getattr(self, attr)
599 if self.company_dependent:
600 # company-dependent fields are mapped to former property fields
601 args['type'] = self.type
602 args['relation'] = self.comodel_name
603 return fields.property(**args)
605 return getattr(fields, self.type)(**args)
607 # properties used by to_column() to create a column instance
608 _column_copy = property(attrgetter('copyable'))
609 _column_select = property(attrgetter('index'))
610 _column_string = property(attrgetter('string'))
611 _column_help = property(attrgetter('help'))
612 _column_readonly = property(attrgetter('readonly'))
613 _column_required = property(attrgetter('required'))
614 _column_states = property(attrgetter('states'))
615 _column_groups = property(attrgetter('groups'))
616 _column_change_default = property(attrgetter('change_default'))
617 _column_deprecated = property(attrgetter('deprecated'))
619 ############################################################################
621 # Conversion of values
625 """ return the null value for this field in the given environment """
628 def convert_to_cache(self, value, record, validate=True):
629 """ convert `value` to the cache level in `env`; `value` may come from
630 an assignment, or have the format of methods :meth:`BaseModel.read`
631 or :meth:`BaseModel.write`
633 :param record: the target record for the assignment, or an empty recordset
635 :param bool validate: when True, field-specific validation of
636 `value` will be performed
640 def convert_to_read(self, value, use_name_get=True):
641 """ convert `value` from the cache to a value as returned by method
642 :meth:`BaseModel.read`
644 :param bool use_name_get: when True, value's diplay name will
645 be computed using :meth:`BaseModel.name_get`, if relevant
648 return False if value is None else value
650 def convert_to_write(self, value, target=None, fnames=None):
651 """ convert `value` from the cache to a valid value for method
652 :meth:`BaseModel.write`.
654 :param target: optional, the record to be modified with this value
655 :param fnames: for relational fields only, an optional collection of
656 field names to convert
658 return self.convert_to_read(value)
660 def convert_to_onchange(self, value):
661 """ convert `value` from the cache to a valid value for an onchange
664 return self.convert_to_write(value)
666 def convert_to_export(self, value, env):
667 """ convert `value` from the cache to a valid value for export. The
668 parameter `env` is given for managing translations.
670 if env.context.get('export_raw_data'):
672 return bool(value) and ustr(value)
674 def convert_to_display_name(self, value):
675 """ convert `value` from the cache to a suitable display name. """
678 ############################################################################
683 def __get__(self, record, owner):
684 """ return the value of field `self` on `record` """
686 return self # the field is accessed through the owner class
689 # null record -> return the null value for this field
690 return self.null(record.env)
692 # only a single record may be accessed
696 return record._cache[self]
700 # cache miss, retrieve value
702 # normal record -> read or compute value for this field
703 self.determine_value(record)
705 # new record -> compute default value for this field
706 record.add_default_value(self)
708 # the result should be in cache now
709 return record._cache[self]
711 def __set__(self, record, value):
712 """ set the value of field `self` on `record` """
715 # only a single record may be updated
718 # adapt value to the cache level
719 value = self.convert_to_cache(value, record)
721 if env.in_draft or not record.id:
722 # determine dependent fields
723 spec = self.modified_draft(record)
725 # set value in cache, inverse field, and mark record as dirty
726 record._cache[self] = value
728 for invf in self.inverse_fields:
729 invf._update(value, record)
732 # determine more dependent fields, and invalidate them
734 spec += self.modified_draft(record)
738 # simply write to the database, and update cache
739 record.write({self.name: self.convert_to_write(value)})
740 record._cache[self] = value
742 ############################################################################
744 # Computation of field values
747 def _compute_value(self, records):
748 """ Invoke the compute method on `records`. """
749 # mark the computed fields failed in cache, so that access before
750 # computation raises an exception
751 exc = Warning("Field %s is accessed before being computed." % self)
752 for field in self.computed_fields:
753 records._cache[field] = FailedValue(exc)
754 records.env.computed[field].update(records._ids)
755 self.compute(records)
756 for field in self.computed_fields:
757 records.env.computed[field].difference_update(records._ids)
759 def compute_value(self, records):
760 """ Invoke the compute method on `records`; the results are in cache. """
761 with records.env.do_in_draft():
763 self._compute_value(records)
765 # some record is missing, retry on existing records only
766 self._compute_value(records.exists())
768 def determine_value(self, record):
769 """ Determine the value of `self` for `record`. """
772 if self.store and not (self.depends and env.in_draft):
773 # this is a stored field
775 # this is a stored computed field, check for recomputation
776 recs = record._recompute_check(self)
778 # recompute the value (only in cache)
779 self.compute_value(recs)
780 # HACK: if result is in the wrong cache, copy values
782 for source, target in zip(recs, recs.with_env(env)):
784 values = target._convert_to_cache({
785 f.name: source[f.name] for f in self.computed_fields
787 except MissingError as e:
788 values = FailedValue(e)
789 target._cache.update(values)
790 # the result is saved to database by BaseModel.recompute()
793 # read the field from database
794 record._prefetch_field(self)
797 # this is either a non-stored computed field, or a stored computed
798 # field in draft mode
800 self.compute_value(record)
802 recs = record._in_cache_without(self)
803 self.compute_value(recs)
806 # this is a non-stored non-computed field
807 record._cache[self] = self.null(env)
809 def determine_default(self, record):
810 """ determine the default value of field `self` on `record` """
812 self._compute_value(record)
814 record._cache[self] = SpecialValue(self.null(record.env))
816 def determine_inverse(self, records):
817 """ Given the value of `self` on `records`, inverse the computation. """
819 self.inverse(records)
821 def determine_domain(self, records, operator, value):
822 """ Return a domain representing a condition on `self`. """
824 return self.search(records, operator, value)
826 return [(self.name, operator, value)]
828 ############################################################################
830 # Notification when fields are modified
833 def modified(self, records):
834 """ Notify that field `self` has been modified on `records`: prepare the
835 fields/records to recompute, and return a spec indicating what to
838 # invalidate the fields that depend on self, and prepare recomputation
839 spec = [(self, records._ids)]
840 for field, path in self._triggers:
841 if path and field.store:
842 # don't move this line to function top, see log
843 env = records.env(user=SUPERUSER_ID, context={'active_test': False})
844 target = env[field.model_name].search([(path, 'in', records.ids)])
846 spec.append((field, target._ids))
847 target.with_env(records.env)._recompute_todo(field)
849 spec.append((field, None))
853 def modified_draft(self, records):
854 """ Same as :meth:`modified`, but in draft mode. """
857 # invalidate the fields on the records in cache that depend on
858 # `records`, except fields currently being computed
860 for field, path in self._triggers:
861 target = env[field.model_name]
862 computed = target.browse(env.computed[field])
864 target = records - computed
866 target = (target.browse(env.cache[field]) - computed).filtered(
867 lambda rec: rec._mapped_cache(path) & records
870 target = target.browse(env.cache[field]) - computed
873 spec.append((field, target._ids))
878 class Boolean(Field):
879 """ Boolean field. """
882 def convert_to_cache(self, value, record, validate=True):
885 def convert_to_export(self, value, env):
886 if env.context.get('export_raw_data'):
891 class Integer(Field):
892 """ Integer field. """
895 def convert_to_cache(self, value, record, validate=True):
896 return int(value or 0)
898 def convert_to_read(self, value, use_name_get=True):
899 # Integer values greater than 2^31-1 are not supported in pure XMLRPC,
900 # so we have to pass them as floats :-(
901 if value and value > xmlrpclib.MAXINT:
905 def _update(self, records, value):
906 # special case, when an integer field is used as inverse for a one2many
907 records._cache[self] = value.id or 0
911 """ Float field. The precision digits are given by the attribute
913 :param digits: a pair (total, decimal), or a function taking a database
914 cursor and returning a pair (total, decimal)
918 _digits = None # digits argument passed to class initializer
919 digits = None # digits as computed by setup()
921 def __init__(self, string=None, digits=None, **kwargs):
922 super(Float, self).__init__(string=string, _digits=digits, **kwargs)
924 def _setup_regular(self, env):
925 super(Float, self)._setup_regular(env)
926 self.digits = self._digits(env.cr) if callable(self._digits) else self._digits
928 _related_digits = property(attrgetter('digits'))
930 _description_digits = property(attrgetter('digits'))
932 _column_digits = property(lambda self: not callable(self._digits) and self._digits)
933 _column_digits_compute = property(lambda self: callable(self._digits) and self._digits)
935 def convert_to_cache(self, value, record, validate=True):
936 # apply rounding here, otherwise value in cache may be wrong!
938 return float_round(float(value or 0.0), precision_digits=self.digits[1])
940 return float(value or 0.0)
943 class _String(Field):
944 """ Abstract class for string fields. """
947 _column_translate = property(attrgetter('translate'))
948 _related_translate = property(attrgetter('translate'))
949 _description_translate = property(attrgetter('translate'))
955 :param size: the maximum size of values stored for that field (integer,
958 :param translate: whether the value of the field has translations
959 (boolean, by default ``False``)
965 _column_size = property(attrgetter('size'))
966 _related_size = property(attrgetter('size'))
967 _description_size = property(attrgetter('size'))
969 def convert_to_cache(self, value, record, validate=True):
970 return bool(value) and ustr(value)[:self.size]
974 """ Text field. Very similar to :class:`Char`, but typically for longer
977 :param translate: whether the value of the field has translations
978 (boolean, by default ``False``)
983 def convert_to_cache(self, value, record, validate=True):
984 return bool(value) and ustr(value)
991 def convert_to_cache(self, value, record, validate=True):
992 return bool(value) and html_sanitize(value)
1001 """ Return the current day in the format expected by the ORM.
1002 This function may be used to compute default values.
1004 return date.today().strftime(DATE_FORMAT)
1007 def context_today(record, timestamp=None):
1008 """ Return the current date as seen in the client's timezone in a format
1009 fit for date fields. This method may be used to compute default
1012 :param datetime timestamp: optional datetime value to use instead of
1013 the current date and time (must be a datetime, regular dates
1014 can't be converted between timezones.)
1017 today = timestamp or datetime.now()
1018 context_today = None
1019 tz_name = record._context.get('tz') or record.env.user.tz
1022 today_utc = pytz.timezone('UTC').localize(today, is_dst=False) # UTC = no DST
1023 context_today = today_utc.astimezone(pytz.timezone(tz_name))
1025 _logger.debug("failed to compute context/client-specific today date, using UTC value for `today`",
1027 return (context_today or today).strftime(DATE_FORMAT)
1030 def from_string(value):
1031 """ Convert an ORM `value` into a :class:`date` value. """
1032 value = value[:DATE_LENGTH]
1033 return datetime.strptime(value, DATE_FORMAT).date()
1036 def to_string(value):
1037 """ Convert a :class:`date` value into the format expected by the ORM. """
1038 return value.strftime(DATE_FORMAT)
1040 def convert_to_cache(self, value, record, validate=True):
1043 if isinstance(value, basestring):
1045 # force parsing for validation
1046 self.from_string(value)
1047 return value[:DATE_LENGTH]
1048 return self.to_string(value)
1050 def convert_to_export(self, value, env):
1051 if value and env.context.get('export_raw_data'):
1052 return self.from_string(value)
1053 return bool(value) and ustr(value)
1056 class Datetime(Field):
1057 """ Datetime field. """
1062 """ Return the current day and time in the format expected by the ORM.
1063 This function may be used to compute default values.
1065 return datetime.now().strftime(DATETIME_FORMAT)
1068 def context_timestamp(record, timestamp):
1069 """Returns the given timestamp converted to the client's timezone.
1070 This method is *not* meant for use as a _defaults initializer,
1071 because datetime fields are automatically converted upon
1072 display on client side. For _defaults you :meth:`fields.datetime.now`
1073 should be used instead.
1075 :param datetime timestamp: naive datetime value (expressed in UTC)
1076 to be converted to the client timezone
1078 :return: timestamp converted to timezone-aware datetime in context
1081 assert isinstance(timestamp, datetime), 'Datetime instance expected'
1082 tz_name = record._context.get('tz') or record.env.user.tz
1085 utc = pytz.timezone('UTC')
1086 context_tz = pytz.timezone(tz_name)
1087 utc_timestamp = utc.localize(timestamp, is_dst=False) # UTC = no DST
1088 return utc_timestamp.astimezone(context_tz)
1090 _logger.debug("failed to compute context/client-specific timestamp, "
1091 "using the UTC value",
1096 def from_string(value):
1097 """ Convert an ORM `value` into a :class:`datetime` value. """
1098 value = value[:DATETIME_LENGTH]
1099 if len(value) == DATE_LENGTH:
1100 value += " 00:00:00"
1101 return datetime.strptime(value, DATETIME_FORMAT)
1104 def to_string(value):
1105 """ Convert a :class:`datetime` value into the format expected by the ORM. """
1106 return value.strftime(DATETIME_FORMAT)
1108 def convert_to_cache(self, value, record, validate=True):
1111 if isinstance(value, basestring):
1113 # force parsing for validation
1114 self.from_string(value)
1115 value = value[:DATETIME_LENGTH]
1116 if len(value) == DATE_LENGTH:
1117 value += " 00:00:00"
1119 return self.to_string(value)
1121 def convert_to_export(self, value, env):
1122 if value and env.context.get('export_raw_data'):
1123 return self.from_string(value)
1124 return bool(value) and ustr(value)
1127 class Binary(Field):
1128 """ Binary field. """
1132 class Selection(Field):
1133 """ Selection field.
1135 :param selection: specifies the possible values for this field.
1136 It is given as either a list of pairs (`value`, `string`), or a
1137 model method, or a method name.
1139 :param selection_add: provides an extension of the selection in the case
1140 of an overridden field. It is a list of pairs (`value`, `string`).
1142 The attribute `selection` is mandatory except in the case of related
1143 fields (see :ref:`field-related`) or field extensions
1144 (see :ref:`field-incremental-definition`).
1147 selection = None # [(value, string), ...], function or method name
1148 selection_add = None # [(value, string), ...]
1150 def __init__(self, selection=None, string=None, **kwargs):
1151 if callable(selection):
1152 from openerp import api
1153 selection = api.expected(api.model, selection)
1154 super(Selection, self).__init__(selection=selection, string=string, **kwargs)
1156 def _setup_related(self, env):
1157 super(Selection, self)._setup_related(env)
1158 # selection must be computed on related field
1159 field = self.related_field
1160 self.selection = lambda model: field._description_selection(model.env)
1162 def _setup_regular(self, env):
1163 super(Selection, self)._setup_regular(env)
1164 # determine selection (applying extensions)
1165 cls = type(env[self.model_name])
1167 for field in resolve_all_mro(cls, self.name, reverse=True):
1168 if isinstance(field, type(self)):
1169 # We cannot use field.selection or field.selection_add here
1170 # because those attributes are overridden by `set_class_name`.
1171 if 'selection' in field._attrs:
1172 selection = field._attrs['selection']
1173 if 'selection_add' in field._attrs:
1174 selection = selection + field._attrs['selection_add']
1177 self.selection = selection
1179 def _description_selection(self, env):
1180 """ return the selection list (pairs (value, label)); labels are
1181 translated according to context language
1183 selection = self.selection
1184 if isinstance(selection, basestring):
1185 return getattr(env[self.model_name], selection)()
1186 if callable(selection):
1187 return selection(env[self.model_name])
1189 # translate selection labels
1191 name = "%s,%s" % (self.model_name, self.name)
1192 translate = partial(
1193 env['ir.translation']._get_source, name, 'selection', env.lang)
1194 return [(value, translate(label)) for value, label in selection]
1199 def _column_selection(self):
1200 if isinstance(self.selection, basestring):
1201 method = self.selection
1202 return lambda self, *a, **kw: getattr(self, method)(*a, **kw)
1204 return self.selection
1206 def get_values(self, env):
1207 """ return a list of the possible values """
1208 selection = self.selection
1209 if isinstance(selection, basestring):
1210 selection = getattr(env[self.model_name], selection)()
1211 elif callable(selection):
1212 selection = selection(env[self.model_name])
1213 return [value for value, _ in selection]
1215 def convert_to_cache(self, value, record, validate=True):
1217 return value or False
1218 if value in self.get_values(record.env):
1222 raise ValueError("Wrong value for %s: %r" % (self, value))
1224 def convert_to_export(self, value, env):
1225 if not isinstance(self.selection, list):
1226 # FIXME: this reproduces an existing buggy behavior!
1228 for item in self._description_selection(env):
1229 if item[0] == value:
1234 class Reference(Selection):
1235 """ Reference field.
1237 :param selection: specifies the possible model names for this field.
1238 It is given as either a list of pairs (`value`, `string`), or a
1239 model method, or a method name.
1241 The attribute `selection` is mandatory except in the case of related
1242 fields (see :ref:`field-related`) or field extensions
1243 (see :ref:`field-incremental-definition`).
1248 def __init__(self, selection=None, string=None, **kwargs):
1249 super(Reference, self).__init__(selection=selection, string=string, **kwargs)
1251 _related_size = property(attrgetter('size'))
1253 _column_size = property(attrgetter('size'))
1255 def convert_to_cache(self, value, record, validate=True):
1256 if isinstance(value, BaseModel):
1257 if ((not validate or value._name in self.get_values(record.env))
1258 and len(value) <= 1):
1259 return value.with_env(record.env) or False
1260 elif isinstance(value, basestring):
1261 res_model, res_id = value.split(',')
1262 return record.env[res_model].browse(int(res_id))
1265 raise ValueError("Wrong value for %s: %r" % (self, value))
1267 def convert_to_read(self, value, use_name_get=True):
1268 return "%s,%s" % (value._name, value.id) if value else False
1270 def convert_to_export(self, value, env):
1271 return bool(value) and value.name_get()[0][1]
1273 def convert_to_display_name(self, value):
1274 return ustr(value and value.display_name)
1277 class _Relational(Field):
1278 """ Abstract class for relational fields. """
1280 domain = None # domain for searching values
1281 context = None # context for searching values
1283 _description_relation = property(attrgetter('comodel_name'))
1284 _description_context = property(attrgetter('context'))
1286 def _description_domain(self, env):
1287 return self.domain(env[self.model_name]) if callable(self.domain) else self.domain
1289 _column_obj = property(attrgetter('comodel_name'))
1290 _column_domain = property(attrgetter('domain'))
1291 _column_context = property(attrgetter('context'))
1293 def null(self, env):
1294 return env[self.comodel_name]
1296 def modified(self, records):
1297 # Invalidate cache for self.inverse_fields, too. Note that recomputation
1298 # of fields that depend on self.inverse_fields is already covered by the
1299 # triggers (see above).
1300 spec = super(_Relational, self).modified(records)
1301 for invf in self.inverse_fields:
1302 spec.append((invf, None))
1306 class Many2one(_Relational):
1307 """ Many2one field; the value of such a field is a recordset of size 0 (no
1308 record) or 1 (a single record).
1310 :param comodel_name: name of the target model (string)
1312 :param domain: an optional domain to set on candidate values on the
1313 client side (domain or string)
1315 :param context: an optional context to use on the client side when
1316 handling that field (dictionary)
1318 :param ondelete: what to do when the referred record is deleted;
1319 possible values are: ``'set null'``, ``'restrict'``, ``'cascade'``
1321 :param auto_join: whether JOINs are generated upon search through that
1322 field (boolean, by default ``False``)
1324 :param delegate: set it to ``True`` to make fields of the target model
1325 accessible from the current model (corresponds to ``_inherits``)
1327 The attribute `comodel_name` is mandatory except in the case of related
1328 fields or field extensions.
1331 ondelete = 'set null' # what to do when value is deleted
1332 auto_join = False # whether joins are generated upon search
1333 delegate = False # whether self implements delegation
1335 def __init__(self, comodel_name=None, string=None, **kwargs):
1336 super(Many2one, self).__init__(comodel_name=comodel_name, string=string, **kwargs)
1338 def _setup_regular(self, env):
1339 super(Many2one, self)._setup_regular(env)
1341 # self.inverse_fields is populated by the corresponding One2many field
1343 # determine self.delegate
1344 self.delegate = self.name in env[self.model_name]._inherits.values()
1346 _column_ondelete = property(attrgetter('ondelete'))
1347 _column_auto_join = property(attrgetter('auto_join'))
1349 def _update(self, records, value):
1350 """ Update the cached value of `self` for `records` with `value`. """
1351 records._cache[self] = value
1353 def convert_to_cache(self, value, record, validate=True):
1354 if isinstance(value, (NoneType, int)):
1355 return record.env[self.comodel_name].browse(value)
1356 if isinstance(value, BaseModel):
1357 if value._name == self.comodel_name and len(value) <= 1:
1358 return value.with_env(record.env)
1359 raise ValueError("Wrong value for %s: %r" % (self, value))
1360 elif isinstance(value, tuple):
1361 return record.env[self.comodel_name].browse(value[0])
1362 elif isinstance(value, dict):
1363 return record.env[self.comodel_name].new(value)
1365 return record.env[self.comodel_name].browse(value)
1367 def convert_to_read(self, value, use_name_get=True):
1368 if use_name_get and value:
1369 # evaluate name_get() as superuser, because the visibility of a
1370 # many2one field value (id and name) depends on the current record's
1371 # access rights, and not the value's access rights.
1372 return value.sudo().name_get()[0]
1376 def convert_to_write(self, value, target=None, fnames=None):
1379 def convert_to_onchange(self, value):
1382 def convert_to_export(self, value, env):
1383 return bool(value) and value.name_get()[0][1]
1385 def convert_to_display_name(self, value):
1386 return ustr(value.display_name)
1388 def determine_default(self, record):
1389 super(Many2one, self).determine_default(record)
1391 # special case: fields that implement inheritance between models
1392 value = record[self.name]
1394 # the default value cannot be null, use a new record instead
1395 record[self.name] = record.env[self.comodel_name].new()
1398 class _RelationalMulti(_Relational):
1399 """ Abstract class for relational fields *2many. """
1401 def _update(self, records, value):
1402 """ Update the cached value of `self` for `records` with `value`. """
1403 for record in records:
1404 record._cache[self] = record[self.name] | value
1406 def convert_to_cache(self, value, record, validate=True):
1407 if isinstance(value, BaseModel):
1408 if value._name == self.comodel_name:
1409 return value.with_env(record.env)
1410 elif isinstance(value, list):
1411 # value is a list of record ids or commands
1413 record = record.browse() # new record has no value
1414 result = record[self.name]
1415 # modify result with the commands;
1416 # beware to not introduce duplicates in result
1417 for command in value:
1418 if isinstance(command, (tuple, list)):
1420 result += result.new(command[2])
1421 elif command[0] == 1:
1422 result.browse(command[1]).update(command[2])
1423 elif command[0] == 2:
1424 # note: the record will be deleted by write()
1425 result -= result.browse(command[1])
1426 elif command[0] == 3:
1427 result -= result.browse(command[1])
1428 elif command[0] == 4:
1429 result += result.browse(command[1]) - result
1430 elif command[0] == 5:
1431 result = result.browse()
1432 elif command[0] == 6:
1433 result = result.browse(command[2])
1434 elif isinstance(command, dict):
1435 result += result.new(command)
1437 result += result.browse(command) - result
1440 return self.null(record.env)
1441 raise ValueError("Wrong value for %s: %s" % (self, value))
1443 def convert_to_read(self, value, use_name_get=True):
1446 def convert_to_write(self, value, target=None, fnames=None):
1447 # remove/delete former records
1450 result = [(6, 0, set_ids)]
1451 add_existing = lambda id: set_ids.append(id)
1453 tag = 2 if self.type == 'one2many' else 3
1454 result = [(tag, record.id) for record in target[self.name] - value]
1455 add_existing = lambda id: result.append((4, id))
1458 # take all fields in cache, except the inverses of self
1459 fnames = set(value._fields) - set(MAGIC_COLUMNS)
1460 for invf in self.inverse_fields:
1461 fnames.discard(invf.name)
1463 # add new and existing records
1464 for record in value:
1465 if not record.id or record._dirty:
1466 values = dict((k, v) for k, v in record._cache.iteritems() if k in fnames)
1467 values = record._convert_to_write(values)
1469 result.append((0, 0, values))
1471 result.append((1, record.id, values))
1473 add_existing(record.id)
1477 def convert_to_export(self, value, env):
1478 return bool(value) and ','.join(name for id, name in value.name_get())
1480 def convert_to_display_name(self, value):
1481 raise NotImplementedError()
1484 class One2many(_RelationalMulti):
1485 """ One2many field; the value of such a field is the recordset of all the
1486 records in `comodel_name` such that the field `inverse_name` is equal to
1489 :param comodel_name: name of the target model (string)
1491 :param inverse_name: name of the inverse `Many2one` field in
1492 `comodel_name` (string)
1494 :param domain: an optional domain to set on candidate values on the
1495 client side (domain or string)
1497 :param context: an optional context to use on the client side when
1498 handling that field (dictionary)
1500 :param auto_join: whether JOINs are generated upon search through that
1501 field (boolean, by default ``False``)
1503 :param limit: optional limit to use upon read (integer)
1505 The attributes `comodel_name` and `inverse_name` are mandatory except in
1506 the case of related fields or field extensions.
1509 inverse_name = None # name of the inverse field
1510 auto_join = False # whether joins are generated upon search
1511 limit = None # optional limit to use upon read
1512 copyable = False # o2m are not copied by default
1514 def __init__(self, comodel_name=None, inverse_name=None, string=None, **kwargs):
1515 super(One2many, self).__init__(
1516 comodel_name=comodel_name,
1517 inverse_name=inverse_name,
1522 def _setup_regular(self, env):
1523 super(One2many, self)._setup_regular(env)
1525 if self.inverse_name:
1526 # link self to its inverse field and vice-versa
1527 invf = env[self.comodel_name]._fields[self.inverse_name]
1528 self.inverse_fields.append(invf)
1529 invf.inverse_fields.append(self)
1531 _description_relation_field = property(attrgetter('inverse_name'))
1533 _column_fields_id = property(attrgetter('inverse_name'))
1534 _column_auto_join = property(attrgetter('auto_join'))
1535 _column_limit = property(attrgetter('limit'))
1538 class Many2many(_RelationalMulti):
1539 """ Many2many field; the value of such a field is the recordset.
1541 :param comodel_name: name of the target model (string)
1543 The attribute `comodel_name` is mandatory except in the case of related
1544 fields or field extensions.
1546 :param relation: optional name of the table that stores the relation in
1547 the database (string)
1549 :param column1: optional name of the column referring to "these" records
1550 in the table `relation` (string)
1552 :param column2: optional name of the column referring to "those" records
1553 in the table `relation` (string)
1555 The attributes `relation`, `column1` and `column2` are optional. If not
1556 given, names are automatically generated from model names, provided
1557 `model_name` and `comodel_name` are different!
1559 :param domain: an optional domain to set on candidate values on the
1560 client side (domain or string)
1562 :param context: an optional context to use on the client side when
1563 handling that field (dictionary)
1565 :param limit: optional limit to use upon read (integer)
1569 relation = None # name of table
1570 column1 = None # column of table referring to model
1571 column2 = None # column of table referring to comodel
1572 limit = None # optional limit to use upon read
1574 def __init__(self, comodel_name=None, relation=None, column1=None, column2=None,
1575 string=None, **kwargs):
1576 super(Many2many, self).__init__(
1577 comodel_name=comodel_name,
1585 def _setup_regular(self, env):
1586 super(Many2many, self)._setup_regular(env)
1588 if self.store and not self.relation:
1589 model = env[self.model_name]
1590 column = model._columns[self.name]
1591 if not isinstance(column, fields.function):
1592 self.relation, self.column1, self.column2 = column._sql_names(model)
1595 m2m = env.registry._m2m
1596 # if inverse field has already been setup, it is present in m2m
1597 invf = m2m.get((self.relation, self.column2, self.column1))
1599 self.inverse_fields.append(invf)
1600 invf.inverse_fields.append(self)
1602 # add self in m2m, so that its inverse field can find it
1603 m2m[(self.relation, self.column1, self.column2)] = self
1605 _column_rel = property(attrgetter('relation'))
1606 _column_id1 = property(attrgetter('column1'))
1607 _column_id2 = property(attrgetter('column2'))
1608 _column_limit = property(attrgetter('limit'))
1612 """ Special case for field 'id'. """
1616 def __init__(self, string=None, **kwargs):
1617 super(Id, self).__init__(type='integer', string=string, **kwargs)
1619 def to_column(self):
1620 return fields.integer('ID')
1622 def __get__(self, record, owner):
1624 return self # the field is accessed through the class owner
1627 return record.ensure_one()._ids[0]
1629 def __set__(self, record, value):
1630 raise TypeError("field 'id' cannot be assigned")
1633 # imported here to avoid dependency cycle issues
1634 from openerp import SUPERUSER_ID
1635 from .exceptions import Warning, MissingError
1636 from .models import BaseModel, MAGIC_COLUMNS
1637 from .osv import fields