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 class MetaField(type):
73 """ Metaclass for field classes. """
76 def __init__(cls, name, bases, attrs):
77 super(MetaField, cls).__init__(name, bases, attrs)
79 cls.by_type[cls.type] = cls
81 # compute class attributes to avoid calling dir() on fields
83 cls.related_attrs = []
84 cls.description_attrs = []
86 if attr.startswith('_column_'):
87 cls.column_attrs.append((attr[8:], attr))
88 elif attr.startswith('_related_'):
89 cls.related_attrs.append((attr[9:], attr))
90 elif attr.startswith('_description_'):
91 cls.description_attrs.append((attr[13:], attr))
95 """ The field descriptor contains the field definition, and manages accesses
96 and assignments of the corresponding field on records. The following
97 attributes may be provided when instanciating a field:
99 :param string: the label of the field seen by users (string); if not
100 set, the ORM takes the field name in the class (capitalized).
102 :param help: the tooltip of the field seen by users (string)
104 :param readonly: whether the field is readonly (boolean, by default ``False``)
106 :param required: whether the value of the field is required (boolean, by
109 :param index: whether the field is indexed in database (boolean, by
112 :param default: the default value for the field; this is either a static
113 value, or a function taking a recordset and returning a value
115 :param states: a dictionary mapping state values to lists of UI attribute-value
116 pairs; possible attributes are: 'readonly', 'required', 'invisible'.
117 Note: Any state-based condition requires the ``state`` field value to be
118 available on the client-side UI. This is typically done by including it in
119 the relevant views, possibly made invisible if not relevant for the
122 :param groups: comma-separated list of group xml ids (string); this
123 restricts the field access to the users of the given groups only
125 :param bool copy: whether the field value should be copied when the record
126 is duplicated (default: ``True`` for normal fields, ``False`` for
127 ``one2many`` and computed fields, including property fields and
132 .. rubric:: Computed fields
134 One can define a field whose value is computed instead of simply being
135 read from the database. The attributes that are specific to computed
136 fields are given below. To define such a field, simply provide a value
137 for the attribute `compute`.
139 :param compute: name of a method that computes the field
141 :param inverse: name of a method that inverses the field (optional)
143 :param search: name of a method that implement search on the field (optional)
145 :param store: whether the field is stored in database (boolean, by
146 default ``False`` on computed fields)
148 The methods given for `compute`, `inverse` and `search` are model
149 methods. Their signature is shown in the following example::
151 upper = fields.Char(compute='_compute_upper',
152 inverse='_inverse_upper',
153 search='_search_upper')
156 def _compute_upper(self):
158 self.upper = self.name.upper() if self.name else False
160 def _inverse_upper(self):
162 self.name = self.upper.lower() if self.upper else False
164 def _search_upper(self, operator, value):
165 if operator == 'like':
167 return [('name', operator, value)]
169 The compute method has to assign the field on all records of the invoked
170 recordset. The decorator :meth:`openerp.api.depends` must be applied on
171 the compute method to specify the field dependencies; those dependencies
172 are used to determine when to recompute the field; recomputation is
173 automatic and guarantees cache/database consistency. Note that the same
174 method can be used for several fields, you simply have to assign all the
175 given fields in the method; the method will be invoked once for all
178 By default, a computed field is not stored to the database, and is
179 computed on-the-fly. Adding the attribute ``store=True`` will store the
180 field's values in the database. The advantage of a stored field is that
181 searching on that field is done by the database itself. The disadvantage
182 is that it requires database updates when the field must be recomputed.
184 The inverse method, as its name says, does the inverse of the compute
185 method: the invoked records have a value for the field, and you must
186 apply the necessary changes on the field dependencies such that the
187 computation gives the expected value. Note that a computed field without
188 an inverse method is readonly by default.
190 The search method is invoked when processing domains before doing an
191 actual search on the model. It must return a domain equivalent to the
192 condition: `field operator value`.
196 .. rubric:: Related fields
198 The value of a related field is given by following a sequence of
199 relational fields and reading a field on the reached model. The complete
200 sequence of fields to traverse is specified by the attribute
202 :param related: sequence of field names
204 The value of some attributes from related fields are automatically taken
205 from the source field, when it makes sense. Examples are the attributes
206 `string` or `selection` on selection fields.
208 By default, the values of related fields are not stored to the database.
209 Add the attribute ``store=True`` to make it stored, just like computed
210 fields. Related fields are automatically recomputed when their
211 dependencies are modified.
213 .. _field-company-dependent:
215 .. rubric:: Company-dependent fields
217 Formerly known as 'property' fields, the value of those fields depends
218 on the company. In other words, users that belong to different companies
219 may see different values for the field on a given record.
221 :param company_dependent: whether the field is company-dependent (boolean)
223 .. _field-incremental-definition:
225 .. rubric:: Incremental definition
227 A field is defined as class attribute on a model class. If the model
228 is extended (see :class:`~openerp.models.Model`), one can also extend
229 the field definition by redefining a field with the same name and same
230 type on the subclass. In that case, the attributes of the field are
231 taken from the parent class and overridden by the ones given in
234 For instance, the second class below only adds a tooltip on the field
237 class First(models.Model):
239 state = fields.Selection([...], required=True)
241 class Second(models.Model):
243 state = fields.Selection(help="Blah blah blah")
246 __metaclass__ = MetaField
248 _attrs = None # dictionary with all field attributes
249 _free_attrs = None # list of semantic-free attribute names
251 automatic = False # whether the field is automatically created ("magic" field)
252 _origin = None # the column or field interfaced by self, if any
254 name = None # name of the field
255 type = None # type of the field (string)
256 relational = False # whether the field is a relational one
257 model_name = None # name of the model of this field
258 comodel_name = None # name of the model of values (if relational)
259 inverse_fields = None # list of inverse fields (objects)
261 store = True # whether the field is stored in database
262 index = False # whether the field is indexed in database
263 manual = False # whether the field is a custom field
264 copyable = True # whether the field is copied over by BaseModel.copy()
265 depends = () # collection of field dependencies
266 recursive = False # whether self depends on itself
267 compute = None # compute(recs) computes field on recs
268 inverse = None # inverse(recs) inverses field on recs
269 search = None # search(recs, operator, value) searches on self
270 related = None # sequence of field names, for related fields
271 related_sudo = True # whether related fields should be read as admin
272 company_dependent = False # whether `self` is company-dependent (property field)
273 default = None # default value (literal or callable)
275 string = None # field label
276 help = None # field tooltip
280 groups = False # csv list of group xml ids
281 change_default = None # whether the field may trigger a "user-onchange"
282 deprecated = None # whether the field is ... deprecated
284 def __init__(self, string=None, **kwargs):
285 kwargs['string'] = string
286 self._attrs = {key: val for key, val in kwargs.iteritems() if val is not None}
287 self._free_attrs = []
289 def copy(self, **kwargs):
290 """ copy(item) -> test
292 make a copy of `self`, possibly modified with parameters `kwargs` """
294 field._attrs = {key: val for key, val in kwargs.iteritems() if val is not None}
295 field._free_attrs = list(self._free_attrs)
298 def set_class_name(self, cls, name):
299 """ Assign the model class and field name of `self`. """
300 self.model_name = cls._name
303 # determine all inherited field attributes
305 for field in resolve_all_mro(cls, name, reverse=True):
306 if isinstance(field, type(self)):
307 attrs.update(field._attrs)
310 attrs.update(self._attrs) # necessary in case self is not in cls
312 # initialize `self` with `attrs`
313 if attrs.get('compute'):
314 # by default, computed fields are not stored, not copied and readonly
315 attrs['store'] = attrs.get('store', False)
316 attrs['copy'] = attrs.get('copy', False)
317 attrs['readonly'] = attrs.get('readonly', not attrs.get('inverse'))
318 if attrs.get('related'):
319 # by default, related fields are not stored
320 attrs['store'] = attrs.get('store', False)
322 # attribute is copyable because there is also a copy() method
323 attrs['copyable'] = attrs.pop('copy')
325 for attr, value in attrs.iteritems():
326 if not hasattr(self, attr):
327 self._free_attrs.append(attr)
328 setattr(self, attr, value)
331 self.string = name.replace('_', ' ').capitalize()
336 return "%s.%s" % (self.model_name, self.name)
339 return "%s.%s" % (self.model_name, self.name)
341 ############################################################################
347 """ Prepare `self` for a new setup. """
348 self._setup_done = False
349 # self._triggers is a set of pairs (field, path) that represents the
350 # computed fields that depend on `self`. When `self` is modified, it
351 # invalidates the cache of each `field`, and registers the records to
352 # recompute based on `path`. See method `modified` below for details.
353 self._triggers = set()
354 self.inverse_fields = []
356 def setup(self, env):
357 """ Complete the setup of `self` (dependencies, recomputation triggers,
358 and other properties). This method is idempotent: it has no effect
359 if `self` has already been set up.
361 if not self._setup_done:
362 self._setup_done = True
365 def _setup(self, env):
366 """ Do the actual setup of `self`. """
368 self._setup_related(env)
370 self._setup_regular(env)
372 # put invalidation/recomputation triggers on field dependencies
373 model = env[self.model_name]
374 for path in self.depends:
375 self._setup_dependency([], model, path.split('.'))
377 # put invalidation triggers on model dependencies
378 for dep_model_name, field_names in model._depends.iteritems():
379 dep_model = env[dep_model_name]
380 for field_name in field_names:
381 field = dep_model._fields[field_name]
382 field._triggers.add((self, None))
385 # Setup of related fields
388 def _setup_related(self, env):
389 """ Setup the attributes of a related field. """
390 # fix the type of self.related if necessary
391 if isinstance(self.related, basestring):
392 self.related = tuple(self.related.split('.'))
394 # determine the related field, and make sure it is set up
395 recs = env[self.model_name]
396 for name in self.related[:-1]:
398 field = self.related_field = recs._fields[self.related[-1]]
401 # check type consistency
402 if self.type != field.type:
403 raise Warning("Type of related field %s is inconsistent with %s" % (self, field))
405 # determine dependencies, compute, inverse, and search
406 self.depends = ('.'.join(self.related),)
407 self.compute = self._compute_related
408 self.inverse = self._inverse_related
409 if field._description_searchable(env):
410 self.search = self._search_related
412 # copy attributes from field to self (string, help, etc.)
413 for attr, prop in self.related_attrs:
414 if not getattr(self, attr):
415 setattr(self, attr, getattr(field, prop))
417 def _compute_related(self, records):
418 """ Compute the related field `self` on `records`. """
419 # when related_sudo, bypass access rights checks when reading values
420 others = records.sudo() if self.related_sudo else records
421 for record, other in zip(records, others):
423 # draft record, do not switch to another environment
425 # traverse the intermediate fields; follow the first record at each step
426 for name in self.related[:-1]:
427 other = other[name][:1]
428 record[self.name] = other[self.related[-1]]
430 def _inverse_related(self, records):
431 """ Inverse the related field `self` on `records`. """
432 for record in records:
434 # traverse the intermediate fields, and keep at most one record
435 for name in self.related[:-1]:
436 other = other[name][:1]
438 other[self.related[-1]] = record[self.name]
440 def _search_related(self, records, operator, value):
441 """ Determine the domain to search on field `self`. """
442 return [('.'.join(self.related), operator, value)]
444 # properties used by _setup_related() to copy values from related field
445 _related_string = property(attrgetter('string'))
446 _related_help = property(attrgetter('help'))
447 _related_readonly = property(attrgetter('readonly'))
448 _related_groups = property(attrgetter('groups'))
451 # Setup of non-related fields
454 def _setup_regular(self, env):
455 """ Setup the attributes of a non-related field. """
456 recs = env[self.model_name]
458 def make_depends(deps):
459 return tuple(deps(recs) if callable(deps) else deps)
461 # convert compute into a callable and determine depends
462 if isinstance(self.compute, basestring):
463 # if the compute method has been overridden, concatenate all their _depends
465 for method in resolve_all_mro(type(recs), self.compute, reverse=True):
466 self.depends += make_depends(getattr(method, '_depends', ()))
467 self.compute = getattr(type(recs), self.compute)
469 self.depends = make_depends(getattr(self.compute, '_depends', ()))
471 # convert inverse and search into callables
472 if isinstance(self.inverse, basestring):
473 self.inverse = getattr(type(recs), self.inverse)
474 if isinstance(self.search, basestring):
475 self.search = getattr(type(recs), self.search)
477 def _setup_dependency(self, path0, model, path1):
478 """ Make `self` depend on `model`; `path0 + path1` is a dependency of
479 `self`, and `path0` is the sequence of field names from `self.model`
483 head, tail = path1[0], path1[1:]
486 # special case: add triggers on all fields of model (except self)
487 fields = set(model._fields.itervalues()) - set([self])
489 fields = [model._fields[head]]
493 _logger.debug("Field %s is recursively defined", self)
494 self.recursive = True
499 #_logger.debug("Add trigger on %s to recompute %s", field, self)
500 field._triggers.add((self, '.'.join(path0 or ['id'])))
502 # add trigger on inverse fields, too
503 for invf in field.inverse_fields:
504 #_logger.debug("Add trigger on %s to recompute %s", invf, self)
505 invf._triggers.add((self, '.'.join(path0 + [head])))
507 # recursively traverse the dependency
509 comodel = env[field.comodel_name]
510 self._setup_dependency(path0 + [head], comodel, tail)
513 def dependents(self):
514 """ Return the computed fields that depend on `self`. """
515 return (field for field, path in self._triggers)
517 ############################################################################
522 def get_description(self, env):
523 """ Return a dictionary that describes the field `self`. """
524 desc = {'type': self.type}
525 for attr, prop in self.description_attrs:
526 value = getattr(self, prop)
529 if value is not None:
534 # properties used by get_description()
536 def _description_store(self, env):
538 # if the corresponding column is a function field, check the column
539 column = env[self.model_name]._columns.get(self.name)
540 return bool(getattr(column, 'store', True))
543 def _description_searchable(self, env):
545 column = env[self.model_name]._columns.get(self.name)
546 return bool(getattr(column, 'store', True)) or \
547 bool(getattr(column, '_fnct_search', False))
548 return bool(self.search)
550 _description_manual = property(attrgetter('manual'))
551 _description_depends = property(attrgetter('depends'))
552 _description_related = property(attrgetter('related'))
553 _description_company_dependent = property(attrgetter('company_dependent'))
554 _description_readonly = property(attrgetter('readonly'))
555 _description_required = property(attrgetter('required'))
556 _description_states = property(attrgetter('states'))
557 _description_groups = property(attrgetter('groups'))
558 _description_change_default = property(attrgetter('change_default'))
559 _description_deprecated = property(attrgetter('deprecated'))
561 def _description_string(self, env):
562 if self.string and env.lang:
563 name = "%s,%s" % (self.model_name, self.name)
564 trans = env['ir.translation']._get_source(name, 'field', env.lang)
565 return trans or self.string
568 def _description_help(self, env):
569 if self.help and env.lang:
570 name = "%s,%s" % (self.model_name, self.name)
571 trans = env['ir.translation']._get_source(name, 'help', env.lang)
572 return trans or self.help
575 ############################################################################
577 # Conversion to column instance
581 """ return a low-level field object corresponding to `self` """
584 assert isinstance(self._origin, fields._column)
585 # some columns are registry-dependent, like float fields (digits);
586 # duplicate them to avoid sharing between registries
587 return copy(self._origin)
589 _logger.debug("Create fields._column for Field %s", self)
591 for attr, prop in self.column_attrs:
592 args[attr] = getattr(self, prop)
593 for attr in self._free_attrs:
594 args[attr] = getattr(self, attr)
596 if self.company_dependent:
597 # company-dependent fields are mapped to former property fields
598 args['type'] = self.type
599 args['relation'] = self.comodel_name
600 return fields.property(**args)
602 return getattr(fields, self.type)(**args)
604 # properties used by to_column() to create a column instance
605 _column_copy = property(attrgetter('copyable'))
606 _column_select = property(attrgetter('index'))
607 _column_manual = property(attrgetter('manual'))
608 _column_string = property(attrgetter('string'))
609 _column_help = property(attrgetter('help'))
610 _column_readonly = property(attrgetter('readonly'))
611 _column_required = property(attrgetter('required'))
612 _column_states = property(attrgetter('states'))
613 _column_groups = property(attrgetter('groups'))
614 _column_change_default = property(attrgetter('change_default'))
615 _column_deprecated = property(attrgetter('deprecated'))
617 ############################################################################
619 # Conversion of values
623 """ return the null value for this field in the given environment """
626 def convert_to_cache(self, value, record, validate=True):
627 """ convert `value` to the cache level in `env`; `value` may come from
628 an assignment, or have the format of methods :meth:`BaseModel.read`
629 or :meth:`BaseModel.write`
631 :param record: the target record for the assignment, or an empty recordset
633 :param bool validate: when True, field-specific validation of
634 `value` will be performed
638 def convert_to_read(self, value, use_name_get=True):
639 """ convert `value` from the cache to a value as returned by method
640 :meth:`BaseModel.read`
642 :param bool use_name_get: when True, value's diplay name will
643 be computed using :meth:`BaseModel.name_get`, if relevant
646 return False if value is None else value
648 def convert_to_write(self, value, target=None, fnames=None):
649 """ convert `value` from the cache to a valid value for method
650 :meth:`BaseModel.write`.
652 :param target: optional, the record to be modified with this value
653 :param fnames: for relational fields only, an optional collection of
654 field names to convert
656 return self.convert_to_read(value)
658 def convert_to_onchange(self, value):
659 """ convert `value` from the cache to a valid value for an onchange
662 return self.convert_to_write(value)
664 def convert_to_export(self, value, env):
665 """ convert `value` from the cache to a valid value for export. The
666 parameter `env` is given for managing translations.
668 if env.context.get('export_raw_data'):
670 return bool(value) and ustr(value)
672 def convert_to_display_name(self, value):
673 """ convert `value` from the cache to a suitable display name. """
676 ############################################################################
681 def __get__(self, record, owner):
682 """ return the value of field `self` on `record` """
684 return self # the field is accessed through the owner class
687 # null record -> return the null value for this field
688 return self.null(record.env)
690 # only a single record may be accessed
694 return record._cache[self]
698 # cache miss, retrieve value
700 # normal record -> read or compute value for this field
701 self.determine_value(record)
703 # new record -> compute default value for this field
704 record.add_default_value(self)
706 # the result should be in cache now
707 return record._cache[self]
709 def __set__(self, record, value):
710 """ set the value of field `self` on `record` """
713 # only a single record may be updated
716 # adapt value to the cache level
717 value = self.convert_to_cache(value, record)
719 if env.in_draft or not record.id:
720 # determine dependent fields
721 spec = self.modified_draft(record)
723 # set value in cache, inverse field, and mark record as dirty
724 record._cache[self] = value
726 for invf in self.inverse_fields:
727 invf._update(value, record)
730 # determine more dependent fields, and invalidate them
732 spec += self.modified_draft(record)
736 # simply write to the database, and update cache
737 record.write({self.name: self.convert_to_write(value)})
738 record._cache[self] = value
740 ############################################################################
742 # Computation of field values
745 def _compute_value(self, records):
746 """ Invoke the compute method on `records`. """
747 # mark the computed fields failed in cache, so that access before
748 # computation raises an exception
749 exc = Warning("Field %s is accessed before being computed." % self)
750 for field in self.computed_fields:
751 records._cache[field] = FailedValue(exc)
752 records.env.computed[field].update(records._ids)
753 self.compute(records)
754 for field in self.computed_fields:
755 records.env.computed[field].difference_update(records._ids)
757 def compute_value(self, records):
758 """ Invoke the compute method on `records`; the results are in cache. """
759 with records.env.do_in_draft():
761 self._compute_value(records)
762 except (AccessError, MissingError):
763 # some record is forbidden or missing, retry record by record
764 for record in records:
766 self._compute_value(record)
767 except Exception as exc:
768 record._cache[self.name] = FailedValue(exc)
770 def determine_value(self, record):
771 """ Determine the value of `self` for `record`. """
774 if self.store and not (self.depends and env.in_draft):
775 # this is a stored field
777 # this is a stored computed field, check for recomputation
778 recs = record._recompute_check(self)
780 # recompute the value (only in cache)
781 self.compute_value(recs)
782 # HACK: if result is in the wrong cache, copy values
784 for source, target in zip(recs, recs.with_env(env)):
786 values = target._convert_to_cache({
787 f.name: source[f.name] for f in self.computed_fields
789 except MissingError as e:
790 values = FailedValue(e)
791 target._cache.update(values)
792 # the result is saved to database by BaseModel.recompute()
795 # read the field from database
796 record._prefetch_field(self)
799 # this is either a non-stored computed field, or a stored computed
800 # field in draft mode
802 self.compute_value(record)
804 recs = record._in_cache_without(self)
805 self.compute_value(recs)
808 # this is a non-stored non-computed field
809 record._cache[self] = self.null(env)
811 def determine_default(self, record):
812 """ determine the default value of field `self` on `record` """
813 if self.default is not None:
814 value = self.default(record) if callable(self.default) else self.default
815 record._cache[self] = self.convert_to_cache(value, record)
817 self._compute_value(record)
819 record._cache[self] = SpecialValue(self.null(record.env))
821 def determine_inverse(self, records):
822 """ Given the value of `self` on `records`, inverse the computation. """
824 self.inverse(records)
826 def determine_domain(self, records, operator, value):
827 """ Return a domain representing a condition on `self`. """
829 return self.search(records, operator, value)
831 return [(self.name, operator, value)]
833 ############################################################################
835 # Notification when fields are modified
838 def modified(self, records):
839 """ Notify that field `self` has been modified on `records`: prepare the
840 fields/records to recompute, and return a spec indicating what to
843 # invalidate the fields that depend on self, and prepare recomputation
844 spec = [(self, records._ids)]
845 for field, path in self._triggers:
846 if path and field.store:
847 # don't move this line to function top, see log
848 env = records.env(user=SUPERUSER_ID, context={'active_test': False})
849 target = env[field.model_name].search([(path, 'in', records.ids)])
851 spec.append((field, target._ids))
852 target.with_env(records.env)._recompute_todo(field)
854 spec.append((field, None))
858 def modified_draft(self, records):
859 """ Same as :meth:`modified`, but in draft mode. """
862 # invalidate the fields on the records in cache that depend on
863 # `records`, except fields currently being computed
865 for field, path in self._triggers:
866 target = env[field.model_name]
867 computed = target.browse(env.computed[field])
869 target = records - computed
871 target = (target.browse(env.cache[field]) - computed).filtered(
872 lambda rec: rec._mapped_cache(path) & records
875 target = target.browse(env.cache[field]) - computed
878 spec.append((field, target._ids))
883 class Boolean(Field):
886 def convert_to_cache(self, value, record, validate=True):
889 def convert_to_export(self, value, env):
890 if env.context.get('export_raw_data'):
895 class Integer(Field):
898 def convert_to_cache(self, value, record, validate=True):
899 if isinstance(value, dict):
900 # special case, when an integer field is used as inverse for a one2many
901 return value.get('id', False)
902 return int(value or 0)
904 def convert_to_read(self, value, use_name_get=True):
905 # Integer values greater than 2^31-1 are not supported in pure XMLRPC,
906 # so we have to pass them as floats :-(
907 if value and value > xmlrpclib.MAXINT:
911 def _update(self, records, value):
912 # special case, when an integer field is used as inverse for a one2many
913 records._cache[self] = value.id or 0
917 """ The precision digits are given by the attribute
919 :param digits: a pair (total, decimal), or a function taking a database
920 cursor and returning a pair (total, decimal)
923 _digits = None # digits argument passed to class initializer
924 digits = None # digits as computed by setup()
926 def __init__(self, string=None, digits=None, **kwargs):
927 super(Float, self).__init__(string=string, _digits=digits, **kwargs)
929 def _setup_digits(self, env):
930 """ Setup the digits for `self` and its corresponding column """
931 self.digits = self._digits(env.cr) if callable(self._digits) else self._digits
933 column = env[self.model_name]._columns[self.name]
934 column.digits_change(env.cr)
936 def _setup_regular(self, env):
937 super(Float, self)._setup_regular(env)
938 self._setup_digits(env)
940 _related_digits = property(attrgetter('digits'))
942 _description_digits = property(attrgetter('digits'))
944 _column_digits = property(lambda self: not callable(self._digits) and self._digits)
945 _column_digits_compute = property(lambda self: callable(self._digits) and self._digits)
947 def convert_to_cache(self, value, record, validate=True):
948 # apply rounding here, otherwise value in cache may be wrong!
950 return float_round(float(value or 0.0), precision_digits=self.digits[1])
952 return float(value or 0.0)
955 class _String(Field):
956 """ Abstract class for string fields. """
959 _column_translate = property(attrgetter('translate'))
960 _related_translate = property(attrgetter('translate'))
961 _description_translate = property(attrgetter('translate'))
965 """ Basic string field, can be length-limited, usually displayed as a
966 single-line string in clients
968 :param int size: the maximum size of values stored for that field
969 :param bool translate: whether the values of this field can be translated
974 _column_size = property(attrgetter('size'))
975 _related_size = property(attrgetter('size'))
976 _description_size = property(attrgetter('size'))
978 def convert_to_cache(self, value, record, validate=True):
979 if value is None or value is False:
981 return ustr(value)[:self.size]
984 """ Text field. Very similar to :class:`~.Char` but used for longer
985 contents and displayed as a multiline text box
987 :param translate: whether the value of this field can be translated
991 def convert_to_cache(self, value, record, validate=True):
992 if value is None or value is False:
998 sanitize = True # whether value must be sanitized
1000 _column_sanitize = property(attrgetter('sanitize'))
1001 _related_sanitize = property(attrgetter('sanitize'))
1002 _description_sanitize = property(attrgetter('sanitize'))
1004 def convert_to_cache(self, value, record, validate=True):
1005 if value is None or value is False:
1007 if validate and self.sanitize:
1008 return html_sanitize(value)
1017 """ Return the current day in the format expected by the ORM.
1018 This function may be used to compute default values.
1020 return date.today().strftime(DATE_FORMAT)
1023 def context_today(record, timestamp=None):
1024 """ Return the current date as seen in the client's timezone in a format
1025 fit for date fields. This method may be used to compute default
1028 :param datetime timestamp: optional datetime value to use instead of
1029 the current date and time (must be a datetime, regular dates
1030 can't be converted between timezones.)
1033 today = timestamp or datetime.now()
1034 context_today = None
1035 tz_name = record._context.get('tz') or record.env.user.tz
1038 today_utc = pytz.timezone('UTC').localize(today, is_dst=False) # UTC = no DST
1039 context_today = today_utc.astimezone(pytz.timezone(tz_name))
1041 _logger.debug("failed to compute context/client-specific today date, using UTC value for `today`",
1043 return (context_today or today).strftime(DATE_FORMAT)
1046 def from_string(value):
1047 """ Convert an ORM `value` into a :class:`date` value. """
1048 value = value[:DATE_LENGTH]
1049 return datetime.strptime(value, DATE_FORMAT).date()
1052 def to_string(value):
1053 """ Convert a :class:`date` value into the format expected by the ORM. """
1054 return value.strftime(DATE_FORMAT)
1056 def convert_to_cache(self, value, record, validate=True):
1059 if isinstance(value, basestring):
1061 # force parsing for validation
1062 self.from_string(value)
1063 return value[:DATE_LENGTH]
1064 return self.to_string(value)
1066 def convert_to_export(self, value, env):
1067 if value and env.context.get('export_raw_data'):
1068 return self.from_string(value)
1069 return bool(value) and ustr(value)
1072 class Datetime(Field):
1077 """ Return the current day and time in the format expected by the ORM.
1078 This function may be used to compute default values.
1080 return datetime.now().strftime(DATETIME_FORMAT)
1083 def context_timestamp(record, timestamp):
1084 """Returns the given timestamp converted to the client's timezone.
1085 This method is *not* meant for use as a _defaults initializer,
1086 because datetime fields are automatically converted upon
1087 display on client side. For _defaults you :meth:`fields.datetime.now`
1088 should be used instead.
1090 :param datetime timestamp: naive datetime value (expressed in UTC)
1091 to be converted to the client timezone
1093 :return: timestamp converted to timezone-aware datetime in context
1096 assert isinstance(timestamp, datetime), 'Datetime instance expected'
1097 tz_name = record._context.get('tz') or record.env.user.tz
1100 utc = pytz.timezone('UTC')
1101 context_tz = pytz.timezone(tz_name)
1102 utc_timestamp = utc.localize(timestamp, is_dst=False) # UTC = no DST
1103 return utc_timestamp.astimezone(context_tz)
1105 _logger.debug("failed to compute context/client-specific timestamp, "
1106 "using the UTC value",
1111 def from_string(value):
1112 """ Convert an ORM `value` into a :class:`datetime` value. """
1113 value = value[:DATETIME_LENGTH]
1114 if len(value) == DATE_LENGTH:
1115 value += " 00:00:00"
1116 return datetime.strptime(value, DATETIME_FORMAT)
1119 def to_string(value):
1120 """ Convert a :class:`datetime` value into the format expected by the ORM. """
1121 return value.strftime(DATETIME_FORMAT)
1123 def convert_to_cache(self, value, record, validate=True):
1126 if isinstance(value, basestring):
1128 # force parsing for validation
1129 self.from_string(value)
1130 value = value[:DATETIME_LENGTH]
1131 if len(value) == DATE_LENGTH:
1132 value += " 00:00:00"
1134 return self.to_string(value)
1136 def convert_to_export(self, value, env):
1137 if value and env.context.get('export_raw_data'):
1138 return self.from_string(value)
1139 return bool(value) and ustr(value)
1142 class Binary(Field):
1146 class Selection(Field):
1148 :param selection: specifies the possible values for this field.
1149 It is given as either a list of pairs (`value`, `string`), or a
1150 model method, or a method name.
1151 :param selection_add: provides an extension of the selection in the case
1152 of an overridden field. It is a list of pairs (`value`, `string`).
1154 The attribute `selection` is mandatory except in the case of
1155 :ref:`related fields <field-related>` or :ref:`field extensions
1156 <field-incremental-definition>`.
1159 selection = None # [(value, string), ...], function or method name
1160 selection_add = None # [(value, string), ...]
1162 def __init__(self, selection=None, string=None, **kwargs):
1163 if callable(selection):
1164 from openerp import api
1165 selection = api.expected(api.model, selection)
1166 super(Selection, self).__init__(selection=selection, string=string, **kwargs)
1168 def _setup_related(self, env):
1169 super(Selection, self)._setup_related(env)
1170 # selection must be computed on related field
1171 field = self.related_field
1172 self.selection = lambda model: field._description_selection(model.env)
1174 def _setup_regular(self, env):
1175 super(Selection, self)._setup_regular(env)
1176 # determine selection (applying extensions)
1177 cls = type(env[self.model_name])
1179 for field in resolve_all_mro(cls, self.name, reverse=True):
1180 if isinstance(field, type(self)):
1181 # We cannot use field.selection or field.selection_add here
1182 # because those attributes are overridden by `set_class_name`.
1183 if 'selection' in field._attrs:
1184 selection = field._attrs['selection']
1185 if 'selection_add' in field._attrs:
1186 selection = selection + field._attrs['selection_add']
1189 self.selection = selection
1191 def _description_selection(self, env):
1192 """ return the selection list (pairs (value, label)); labels are
1193 translated according to context language
1195 selection = self.selection
1196 if isinstance(selection, basestring):
1197 return getattr(env[self.model_name], selection)()
1198 if callable(selection):
1199 return selection(env[self.model_name])
1201 # translate selection labels
1203 name = "%s,%s" % (self.model_name, self.name)
1204 translate = partial(
1205 env['ir.translation']._get_source, name, 'selection', env.lang)
1206 return [(value, translate(label)) for value, label in selection]
1211 def _column_selection(self):
1212 if isinstance(self.selection, basestring):
1213 method = self.selection
1214 return lambda self, *a, **kw: getattr(self, method)(*a, **kw)
1216 return self.selection
1218 def get_values(self, env):
1219 """ return a list of the possible values """
1220 selection = self.selection
1221 if isinstance(selection, basestring):
1222 selection = getattr(env[self.model_name], selection)()
1223 elif callable(selection):
1224 selection = selection(env[self.model_name])
1225 return [value for value, _ in selection]
1227 def convert_to_cache(self, value, record, validate=True):
1229 return value or False
1230 if value in self.get_values(record.env):
1234 raise ValueError("Wrong value for %s: %r" % (self, value))
1236 def convert_to_export(self, value, env):
1237 if not isinstance(self.selection, list):
1238 # FIXME: this reproduces an existing buggy behavior!
1240 for item in self._description_selection(env):
1241 if item[0] == value:
1246 class Reference(Selection):
1250 def __init__(self, selection=None, string=None, **kwargs):
1251 super(Reference, self).__init__(selection=selection, string=string, **kwargs)
1253 _related_size = property(attrgetter('size'))
1255 _column_size = property(attrgetter('size'))
1257 def convert_to_cache(self, value, record, validate=True):
1258 if isinstance(value, BaseModel):
1259 if ((not validate or value._name in self.get_values(record.env))
1260 and len(value) <= 1):
1261 return value.with_env(record.env) or False
1262 elif isinstance(value, basestring):
1263 res_model, res_id = value.split(',')
1264 return record.env[res_model].browse(int(res_id))
1267 raise ValueError("Wrong value for %s: %r" % (self, value))
1269 def convert_to_read(self, value, use_name_get=True):
1270 return "%s,%s" % (value._name, value.id) if value else False
1272 def convert_to_export(self, value, env):
1273 return bool(value) and value.name_get()[0][1]
1275 def convert_to_display_name(self, value):
1276 return ustr(value and value.display_name)
1279 class _Relational(Field):
1280 """ Abstract class for relational fields. """
1282 domain = None # domain for searching values
1283 context = None # context for searching values
1285 _description_relation = property(attrgetter('comodel_name'))
1286 _description_context = property(attrgetter('context'))
1288 def _description_domain(self, env):
1289 return self.domain(env[self.model_name]) if callable(self.domain) else self.domain
1291 _column_obj = property(attrgetter('comodel_name'))
1292 _column_domain = property(attrgetter('domain'))
1293 _column_context = property(attrgetter('context'))
1295 def null(self, env):
1296 return env[self.comodel_name]
1298 def modified(self, records):
1299 # Invalidate cache for self.inverse_fields, too. Note that recomputation
1300 # of fields that depend on self.inverse_fields is already covered by the
1301 # triggers (see above).
1302 spec = super(_Relational, self).modified(records)
1303 for invf in self.inverse_fields:
1304 spec.append((invf, None))
1308 class Many2one(_Relational):
1309 """ The value of such a field is a recordset of size 0 (no
1310 record) or 1 (a single record).
1312 :param comodel_name: name of the target model (string)
1314 :param domain: an optional domain to set on candidate values on the
1315 client side (domain or string)
1317 :param context: an optional context to use on the client side when
1318 handling that field (dictionary)
1320 :param ondelete: what to do when the referred record is deleted;
1321 possible values are: ``'set null'``, ``'restrict'``, ``'cascade'``
1323 :param auto_join: whether JOINs are generated upon search through that
1324 field (boolean, by default ``False``)
1326 :param delegate: set it to ``True`` to make fields of the target model
1327 accessible from the current model (corresponds to ``_inherits``)
1329 The attribute `comodel_name` is mandatory except in the case of related
1330 fields or field extensions.
1333 ondelete = 'set null' # what to do when value is deleted
1334 auto_join = False # whether joins are generated upon search
1335 delegate = False # whether self implements delegation
1337 def __init__(self, comodel_name=None, string=None, **kwargs):
1338 super(Many2one, self).__init__(comodel_name=comodel_name, string=string, **kwargs)
1340 def _setup_regular(self, env):
1341 super(Many2one, self)._setup_regular(env)
1343 # self.inverse_fields is populated by the corresponding One2many field
1345 # determine self.delegate
1346 self.delegate = self.name in env[self.model_name]._inherits.values()
1348 _column_ondelete = property(attrgetter('ondelete'))
1349 _column_auto_join = property(attrgetter('auto_join'))
1351 def _update(self, records, value):
1352 """ Update the cached value of `self` for `records` with `value`. """
1353 records._cache[self] = value
1355 def convert_to_cache(self, value, record, validate=True):
1356 if isinstance(value, (NoneType, int)):
1357 return record.env[self.comodel_name].browse(value)
1358 if isinstance(value, BaseModel):
1359 if value._name == self.comodel_name and len(value) <= 1:
1360 return value.with_env(record.env)
1361 raise ValueError("Wrong value for %s: %r" % (self, value))
1362 elif isinstance(value, tuple):
1363 return record.env[self.comodel_name].browse(value[0])
1364 elif isinstance(value, dict):
1365 return record.env[self.comodel_name].new(value)
1367 return record.env[self.comodel_name].browse(value)
1369 def convert_to_read(self, value, use_name_get=True):
1370 if use_name_get and value:
1371 # evaluate name_get() as superuser, because the visibility of a
1372 # many2one field value (id and name) depends on the current record's
1373 # access rights, and not the value's access rights.
1375 return value.sudo().name_get()[0]
1376 except MissingError:
1377 # Should not happen, unless the foreign key is missing.
1382 def convert_to_write(self, value, target=None, fnames=None):
1385 def convert_to_onchange(self, value):
1388 def convert_to_export(self, value, env):
1389 return bool(value) and value.name_get()[0][1]
1391 def convert_to_display_name(self, value):
1392 return ustr(value.display_name)
1394 def determine_default(self, record):
1395 super(Many2one, self).determine_default(record)
1397 # special case: fields that implement inheritance between models
1398 value = record[self.name]
1400 # the default value cannot be null, use a new record instead
1401 record[self.name] = record.env[self.comodel_name].new()
1404 class UnionUpdate(SpecialValue):
1405 """ Placeholder for a value update; when this value is taken from the cache,
1406 it returns ``record[field.name] | value`` and stores it in the cache.
1408 def __init__(self, field, record, value):
1409 self.args = (field, record, value)
1412 field, record, value = self.args
1413 # in order to read the current field's value, remove self from cache
1414 del record._cache[field]
1415 # read the current field's value, and update it in cache only
1416 record._cache[field] = new_value = record[field.name] | value
1420 class _RelationalMulti(_Relational):
1421 """ Abstract class for relational fields *2many. """
1423 def _update(self, records, value):
1424 """ Update the cached value of `self` for `records` with `value`. """
1425 for record in records:
1426 if self in record._cache:
1427 record._cache[self] = record[self.name] | value
1429 record._cache[self] = UnionUpdate(self, record, value)
1431 def convert_to_cache(self, value, record, validate=True):
1432 if isinstance(value, BaseModel):
1433 if value._name == self.comodel_name:
1434 return value.with_env(record.env)
1435 elif isinstance(value, list):
1436 # value is a list of record ids or commands
1438 record = record.browse() # new record has no value
1439 result = record[self.name]
1440 # modify result with the commands;
1441 # beware to not introduce duplicates in result
1442 for command in value:
1443 if isinstance(command, (tuple, list)):
1445 result += result.new(command[2])
1446 elif command[0] == 1:
1447 result.browse(command[1]).update(command[2])
1448 result += result.browse(command[1]) - result
1449 elif command[0] == 2:
1450 # note: the record will be deleted by write()
1451 result -= result.browse(command[1])
1452 elif command[0] == 3:
1453 result -= result.browse(command[1])
1454 elif command[0] == 4:
1455 result += result.browse(command[1]) - result
1456 elif command[0] == 5:
1457 result = result.browse()
1458 elif command[0] == 6:
1459 result = result.browse(command[2])
1460 elif isinstance(command, dict):
1461 result += result.new(command)
1463 result += result.browse(command) - result
1466 return self.null(record.env)
1467 raise ValueError("Wrong value for %s: %s" % (self, value))
1469 def convert_to_read(self, value, use_name_get=True):
1472 def convert_to_write(self, value, target=None, fnames=None):
1473 # remove/delete former records
1476 result = [(6, 0, set_ids)]
1477 add_existing = lambda id: set_ids.append(id)
1479 tag = 2 if self.type == 'one2many' else 3
1480 result = [(tag, record.id) for record in target[self.name] - value]
1481 add_existing = lambda id: result.append((4, id))
1484 # take all fields in cache, except the inverses of self
1485 fnames = set(value._fields) - set(MAGIC_COLUMNS)
1486 for invf in self.inverse_fields:
1487 fnames.discard(invf.name)
1489 # add new and existing records
1490 for record in value:
1491 if not record.id or record._dirty:
1492 values = dict((k, v) for k, v in record._cache.iteritems() if k in fnames)
1493 values = record._convert_to_write(values)
1495 result.append((0, 0, values))
1497 result.append((1, record.id, values))
1499 add_existing(record.id)
1503 def convert_to_export(self, value, env):
1504 return bool(value) and ','.join(name for id, name in value.name_get())
1506 def convert_to_display_name(self, value):
1507 raise NotImplementedError()
1509 def _compute_related(self, records):
1510 """ Compute the related field `self` on `records`. """
1511 for record in records:
1513 # traverse the intermediate fields, and keep at most one record
1514 for name in self.related[:-1]:
1515 value = value[name][:1]
1516 record[self.name] = value[self.related[-1]]
1519 class One2many(_RelationalMulti):
1520 """ One2many field; the value of such a field is the recordset of all the
1521 records in `comodel_name` such that the field `inverse_name` is equal to
1524 :param comodel_name: name of the target model (string)
1526 :param inverse_name: name of the inverse `Many2one` field in
1527 `comodel_name` (string)
1529 :param domain: an optional domain to set on candidate values on the
1530 client side (domain or string)
1532 :param context: an optional context to use on the client side when
1533 handling that field (dictionary)
1535 :param auto_join: whether JOINs are generated upon search through that
1536 field (boolean, by default ``False``)
1538 :param limit: optional limit to use upon read (integer)
1540 The attributes `comodel_name` and `inverse_name` are mandatory except in
1541 the case of related fields or field extensions.
1544 inverse_name = None # name of the inverse field
1545 auto_join = False # whether joins are generated upon search
1546 limit = None # optional limit to use upon read
1547 copyable = False # o2m are not copied by default
1549 def __init__(self, comodel_name=None, inverse_name=None, string=None, **kwargs):
1550 super(One2many, self).__init__(
1551 comodel_name=comodel_name,
1552 inverse_name=inverse_name,
1557 def _setup_regular(self, env):
1558 super(One2many, self)._setup_regular(env)
1560 if self.inverse_name:
1561 # link self to its inverse field and vice-versa
1562 invf = env[self.comodel_name]._fields[self.inverse_name]
1563 # In some rare cases, a `One2many` field can link to `Int` field
1564 # (res_model/res_id pattern). Only inverse the field if this is
1565 # a `Many2one` field.
1566 if isinstance(invf, Many2one):
1567 self.inverse_fields.append(invf)
1568 invf.inverse_fields.append(self)
1570 _description_relation_field = property(attrgetter('inverse_name'))
1572 _column_fields_id = property(attrgetter('inverse_name'))
1573 _column_auto_join = property(attrgetter('auto_join'))
1574 _column_limit = property(attrgetter('limit'))
1577 class Many2many(_RelationalMulti):
1578 """ Many2many field; the value of such a field is the recordset.
1580 :param comodel_name: name of the target model (string)
1582 The attribute `comodel_name` is mandatory except in the case of related
1583 fields or field extensions.
1585 :param relation: optional name of the table that stores the relation in
1586 the database (string)
1588 :param column1: optional name of the column referring to "these" records
1589 in the table `relation` (string)
1591 :param column2: optional name of the column referring to "those" records
1592 in the table `relation` (string)
1594 The attributes `relation`, `column1` and `column2` are optional. If not
1595 given, names are automatically generated from model names, provided
1596 `model_name` and `comodel_name` are different!
1598 :param domain: an optional domain to set on candidate values on the
1599 client side (domain or string)
1601 :param context: an optional context to use on the client side when
1602 handling that field (dictionary)
1604 :param limit: optional limit to use upon read (integer)
1608 relation = None # name of table
1609 column1 = None # column of table referring to model
1610 column2 = None # column of table referring to comodel
1611 limit = None # optional limit to use upon read
1613 def __init__(self, comodel_name=None, relation=None, column1=None, column2=None,
1614 string=None, **kwargs):
1615 super(Many2many, self).__init__(
1616 comodel_name=comodel_name,
1624 def _setup_regular(self, env):
1625 super(Many2many, self)._setup_regular(env)
1627 if self.store and not self.relation:
1628 model = env[self.model_name]
1629 column = model._columns[self.name]
1630 if not isinstance(column, fields.function):
1631 self.relation, self.column1, self.column2 = column._sql_names(model)
1634 m2m = env.registry._m2m
1635 # if inverse field has already been setup, it is present in m2m
1636 invf = m2m.get((self.relation, self.column2, self.column1))
1638 self.inverse_fields.append(invf)
1639 invf.inverse_fields.append(self)
1641 # add self in m2m, so that its inverse field can find it
1642 m2m[(self.relation, self.column1, self.column2)] = self
1644 _column_rel = property(attrgetter('relation'))
1645 _column_id1 = property(attrgetter('column1'))
1646 _column_id2 = property(attrgetter('column2'))
1647 _column_limit = property(attrgetter('limit'))
1651 """ Special case for field 'id'. """
1653 #: Can't write this!
1656 def __init__(self, string=None, **kwargs):
1657 super(Id, self).__init__(type='integer', string=string, **kwargs)
1659 def to_column(self):
1660 """ to_column() -> fields._column
1664 return fields.integer('ID')
1666 def __get__(self, record, owner):
1668 return self # the field is accessed through the class owner
1671 return record.ensure_one()._ids[0]
1673 def __set__(self, record, value):
1674 raise TypeError("field 'id' cannot be assigned")
1677 # imported here to avoid dependency cycle issues
1678 from openerp import SUPERUSER_ID
1679 from .exceptions import Warning, AccessError, MissingError
1680 from .models import BaseModel, MAGIC_COLUMNS
1681 from .osv import fields