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 inherited = False # whether the field is inherited (_inherits)
253 column = None # the column interfaced by the field
254 setup_done = False # whether the field has been set up
256 name = None # name of the field
257 type = None # type of the field (string)
258 relational = False # whether the field is a relational one
259 model_name = None # name of the model of this field
260 comodel_name = None # name of the model of values (if relational)
261 inverse_fields = None # list of inverse fields (objects)
263 store = True # whether the field is stored in database
264 index = False # whether the field is indexed in database
265 manual = False # whether the field is a custom field
266 copyable = True # whether the field is copied over by BaseModel.copy()
267 depends = () # collection of field dependencies
268 recursive = False # whether self depends on itself
269 compute = None # compute(recs) computes field on recs
270 inverse = None # inverse(recs) inverses field on recs
271 search = None # search(recs, operator, value) searches on self
272 related = None # sequence of field names, for related fields
273 related_sudo = True # whether related fields should be read as admin
274 company_dependent = False # whether `self` is company-dependent (property field)
275 default = None # default(recs) returns the default value
277 string = None # field label
278 help = None # field tooltip
282 groups = False # csv list of group xml ids
283 change_default = None # whether the field may trigger a "user-onchange"
284 deprecated = None # whether the field is ... deprecated
286 def __init__(self, string=None, **kwargs):
287 kwargs['string'] = string
288 self._attrs = {key: val for key, val in kwargs.iteritems() if val is not None}
289 self._free_attrs = []
291 def copy(self, **kwargs):
292 """ copy(item) -> test
294 make a copy of `self`, possibly modified with parameters `kwargs` """
296 field._attrs = {key: val for key, val in kwargs.iteritems() if val is not None}
297 field._free_attrs = list(self._free_attrs)
300 def set_class_name(self, cls, name):
301 """ Assign the model class and field name of `self`. """
302 self.model_name = cls._name
305 # determine all inherited field attributes
307 for field in resolve_all_mro(cls, name, reverse=True):
308 if isinstance(field, type(self)):
309 attrs.update(field._attrs)
312 attrs.update(self._attrs) # necessary in case self is not in cls
314 # initialize `self` with `attrs`
315 if 'default' in attrs and not callable(attrs['default']):
316 # make default callable
317 value = attrs['default']
318 attrs['default'] = lambda recs: value
319 if attrs.get('compute'):
320 # by default, computed fields are not stored, not copied and readonly
321 attrs['store'] = attrs.get('store', False)
322 attrs['copy'] = attrs.get('copy', False)
323 attrs['readonly'] = attrs.get('readonly', not attrs.get('inverse'))
324 if attrs.get('related'):
325 # by default, related fields are not stored
326 attrs['store'] = attrs.get('store', False)
328 # attribute is copyable because there is also a copy() method
329 attrs['copyable'] = attrs.pop('copy')
331 for attr, value in attrs.iteritems():
332 if not hasattr(self, attr):
333 self._free_attrs.append(attr)
334 setattr(self, attr, value)
337 self.string = name.replace('_', ' ').capitalize()
342 return "%s.%s" % (self.model_name, self.name)
345 return "%s.%s" % (self.model_name, self.name)
347 ############################################################################
353 """ Prepare `self` for a new setup. """
354 self.setup_done = False
355 # self._triggers is a set of pairs (field, path) that represents the
356 # computed fields that depend on `self`. When `self` is modified, it
357 # invalidates the cache of each `field`, and registers the records to
358 # recompute based on `path`. See method `modified` below for details.
359 self._triggers = set()
360 self.inverse_fields = []
362 def setup(self, env):
363 """ Complete the setup of `self` (dependencies, recomputation triggers,
364 and other properties). This method is idempotent: it has no effect
365 if `self` has already been set up.
367 if not self.setup_done:
368 self.setup_done = True
371 def _setup(self, env):
372 """ Do the actual setup of `self`. """
374 self._setup_related(env)
376 self._setup_regular(env)
378 # put invalidation/recomputation triggers on field dependencies
379 model = env[self.model_name]
380 for path in self.depends:
381 self._setup_dependency([], model, path.split('.'))
383 # put invalidation triggers on model dependencies
384 for dep_model_name, field_names in model._depends.iteritems():
385 dep_model = env[dep_model_name]
386 for field_name in field_names:
387 field = dep_model._fields[field_name]
388 field._triggers.add((self, None))
391 # Setup of related fields
394 def _setup_related(self, env):
395 """ Setup the attributes of a related field. """
396 # fix the type of self.related if necessary
397 if isinstance(self.related, basestring):
398 self.related = tuple(self.related.split('.'))
400 # determine the related field, and make sure it is set up
401 recs = env[self.model_name]
402 for name in self.related[:-1]:
404 field = self.related_field = recs._fields[self.related[-1]]
407 # check type consistency
408 if self.type != field.type:
409 raise Warning("Type of related field %s is inconsistent with %s" % (self, field))
411 # determine dependencies, compute, inverse, and search
412 self.depends = ('.'.join(self.related),)
413 self.compute = self._compute_related
414 self.inverse = self._inverse_related
415 if field._description_searchable(env):
416 # allow searching on self only if the related field is searchable
417 self.search = self._search_related
419 # copy attributes from field to self (string, help, etc.)
420 for attr, prop in self.related_attrs:
421 if not getattr(self, attr):
422 setattr(self, attr, getattr(field, prop))
424 def _compute_related(self, records):
425 """ Compute the related field `self` on `records`. """
426 # when related_sudo, bypass access rights checks when reading values
427 others = records.sudo() if self.related_sudo else records
428 for record, other in zip(records, others):
430 # draft record, do not switch to another environment
432 # traverse the intermediate fields; follow the first record at each step
433 for name in self.related[:-1]:
434 other = other[name][:1]
435 record[self.name] = other[self.related[-1]]
437 def _inverse_related(self, records):
438 """ Inverse the related field `self` on `records`. """
439 for record in records:
441 # traverse the intermediate fields, and keep at most one record
442 for name in self.related[:-1]:
443 other = other[name][:1]
445 other[self.related[-1]] = record[self.name]
447 def _search_related(self, records, operator, value):
448 """ Determine the domain to search on field `self`. """
449 return [('.'.join(self.related), operator, value)]
451 # properties used by _setup_related() to copy values from related field
452 _related_string = property(attrgetter('string'))
453 _related_help = property(attrgetter('help'))
454 _related_readonly = property(attrgetter('readonly'))
455 _related_groups = property(attrgetter('groups'))
458 # Setup of non-related fields
461 def _setup_regular(self, env):
462 """ Setup the attributes of a non-related field. """
463 recs = env[self.model_name]
465 def make_depends(deps):
466 return tuple(deps(recs) if callable(deps) else deps)
468 # convert compute into a callable and determine depends
469 if isinstance(self.compute, basestring):
470 # if the compute method has been overridden, concatenate all their _depends
472 for method in resolve_all_mro(type(recs), self.compute, reverse=True):
473 self.depends += make_depends(getattr(method, '_depends', ()))
474 self.compute = getattr(type(recs), self.compute)
476 self.depends = make_depends(getattr(self.compute, '_depends', ()))
478 # convert inverse and search into callables
479 if isinstance(self.inverse, basestring):
480 self.inverse = getattr(type(recs), self.inverse)
481 if isinstance(self.search, basestring):
482 self.search = getattr(type(recs), self.search)
484 def _setup_dependency(self, path0, model, path1):
485 """ Make `self` depend on `model`; `path0 + path1` is a dependency of
486 `self`, and `path0` is the sequence of field names from `self.model`
490 head, tail = path1[0], path1[1:]
493 # special case: add triggers on all fields of model (except self)
494 fields = set(model._fields.itervalues()) - set([self])
496 fields = [model._fields[head]]
500 _logger.debug("Field %s is recursively defined", self)
501 self.recursive = True
506 #_logger.debug("Add trigger on %s to recompute %s", field, self)
507 field._triggers.add((self, '.'.join(path0 or ['id'])))
509 # add trigger on inverse fields, too
510 for invf in field.inverse_fields:
511 #_logger.debug("Add trigger on %s to recompute %s", invf, self)
512 invf._triggers.add((self, '.'.join(path0 + [head])))
514 # recursively traverse the dependency
516 comodel = env[field.comodel_name]
517 self._setup_dependency(path0 + [head], comodel, tail)
520 def dependents(self):
521 """ Return the computed fields that depend on `self`. """
522 return (field for field, path in self._triggers)
524 ############################################################################
529 def get_description(self, env):
530 """ Return a dictionary that describes the field `self`. """
531 desc = {'type': self.type}
532 for attr, prop in self.description_attrs:
533 value = getattr(self, prop)
536 if value is not None:
541 # properties used by get_description()
543 def _description_store(self, env):
545 # if the corresponding column is a function field, check the column
546 column = env[self.model_name]._columns.get(self.name)
547 return bool(getattr(column, 'store', True))
550 def _description_searchable(self, env):
552 column = env[self.model_name]._columns.get(self.name)
553 return bool(getattr(column, 'store', True)) or \
554 bool(getattr(column, '_fnct_search', False))
555 return bool(self.search)
557 def _description_sortable(self, env):
559 column = env[self.model_name]._columns.get(self.name)
560 return bool(getattr(column, 'store', True))
562 # self is sortable if the inherited field is itself sortable
563 return self.related_field._description_sortable(env)
566 _description_manual = property(attrgetter('manual'))
567 _description_depends = property(attrgetter('depends'))
568 _description_related = property(attrgetter('related'))
569 _description_company_dependent = property(attrgetter('company_dependent'))
570 _description_readonly = property(attrgetter('readonly'))
571 _description_required = property(attrgetter('required'))
572 _description_states = property(attrgetter('states'))
573 _description_groups = property(attrgetter('groups'))
574 _description_change_default = property(attrgetter('change_default'))
575 _description_deprecated = property(attrgetter('deprecated'))
577 def _description_string(self, env):
578 if self.string and env.lang:
579 name = "%s,%s" % (self.model_name, self.name)
580 trans = env['ir.translation']._get_source(name, 'field', env.lang)
581 return trans or self.string
584 def _description_help(self, env):
585 if self.help and env.lang:
586 name = "%s,%s" % (self.model_name, self.name)
587 trans = env['ir.translation']._get_source(name, 'help', env.lang)
588 return trans or self.help
591 ############################################################################
593 # Conversion to column instance
597 """ return a low-level field object corresponding to `self` """
600 # some columns are registry-dependent, like float fields (digits);
601 # duplicate them to avoid sharing between registries
602 _logger.debug("Create fields._column for Field %s", self)
604 for attr, prop in self.column_attrs:
605 args[attr] = getattr(self, prop)
606 for attr in self._free_attrs:
607 args[attr] = getattr(self, attr)
609 if self.company_dependent:
610 # company-dependent fields are mapped to former property fields
611 args['type'] = self.type
612 args['relation'] = self.comodel_name
613 return fields.property(**args)
615 if isinstance(self.column, fields.function):
616 # it is too tricky to recreate a function field, so for that case,
617 # we make a stupid (and possibly incorrect) copy of the column
618 return copy(self.column)
620 return getattr(fields, self.type)(**args)
622 # properties used by to_column() to create a column instance
623 _column_copy = property(attrgetter('copyable'))
624 _column_select = property(attrgetter('index'))
625 _column_manual = property(attrgetter('manual'))
626 _column_string = property(attrgetter('string'))
627 _column_help = property(attrgetter('help'))
628 _column_readonly = property(attrgetter('readonly'))
629 _column_required = property(attrgetter('required'))
630 _column_states = property(attrgetter('states'))
631 _column_groups = property(attrgetter('groups'))
632 _column_change_default = property(attrgetter('change_default'))
633 _column_deprecated = property(attrgetter('deprecated'))
635 ############################################################################
637 # Conversion of values
641 """ return the null value for this field in the given environment """
644 def convert_to_cache(self, value, record, validate=True):
645 """ convert `value` to the cache level in `env`; `value` may come from
646 an assignment, or have the format of methods :meth:`BaseModel.read`
647 or :meth:`BaseModel.write`
649 :param record: the target record for the assignment, or an empty recordset
651 :param bool validate: when True, field-specific validation of
652 `value` will be performed
656 def convert_to_read(self, value, use_name_get=True):
657 """ convert `value` from the cache to a value as returned by method
658 :meth:`BaseModel.read`
660 :param bool use_name_get: when True, value's diplay name will
661 be computed using :meth:`BaseModel.name_get`, if relevant
664 return False if value is None else value
666 def convert_to_write(self, value, target=None, fnames=None):
667 """ convert `value` from the cache to a valid value for method
668 :meth:`BaseModel.write`.
670 :param target: optional, the record to be modified with this value
671 :param fnames: for relational fields only, an optional collection of
672 field names to convert
674 return self.convert_to_read(value)
676 def convert_to_onchange(self, value):
677 """ convert `value` from the cache to a valid value for an onchange
680 return self.convert_to_write(value)
682 def convert_to_export(self, value, env):
683 """ convert `value` from the cache to a valid value for export. The
684 parameter `env` is given for managing translations.
686 if env.context.get('export_raw_data'):
688 return bool(value) and ustr(value)
690 def convert_to_display_name(self, value):
691 """ convert `value` from the cache to a suitable display name. """
694 ############################################################################
699 def __get__(self, record, owner):
700 """ return the value of field `self` on `record` """
702 return self # the field is accessed through the owner class
705 # null record -> return the null value for this field
706 return self.null(record.env)
708 # only a single record may be accessed
712 return record._cache[self]
716 # cache miss, retrieve value
718 # normal record -> read or compute value for this field
719 self.determine_value(record)
721 # new record -> compute default value for this field
722 record.add_default_value(self)
724 # the result should be in cache now
725 return record._cache[self]
727 def __set__(self, record, value):
728 """ set the value of field `self` on `record` """
731 # only a single record may be updated
734 # adapt value to the cache level
735 value = self.convert_to_cache(value, record)
737 if env.in_draft or not record.id:
738 # determine dependent fields
739 spec = self.modified_draft(record)
741 # set value in cache, inverse field, and mark record as dirty
742 record._cache[self] = value
744 for invf in self.inverse_fields:
745 invf._update(value, record)
748 # determine more dependent fields, and invalidate them
750 spec += self.modified_draft(record)
754 # simply write to the database, and update cache
755 record.write({self.name: self.convert_to_write(value)})
756 record._cache[self] = value
758 ############################################################################
760 # Computation of field values
763 def _compute_value(self, records):
764 """ Invoke the compute method on `records`. """
765 # mark the computed fields failed in cache, so that access before
766 # computation raises an exception
767 exc = Warning("Field %s is accessed before being computed." % self)
768 for field in self.computed_fields:
769 records._cache[field] = FailedValue(exc)
770 records.env.computed[field].update(records._ids)
771 self.compute(records)
772 for field in self.computed_fields:
773 records.env.computed[field].difference_update(records._ids)
775 def compute_value(self, records):
776 """ Invoke the compute method on `records`; the results are in cache. """
777 with records.env.do_in_draft():
779 self._compute_value(records)
780 except (AccessError, MissingError):
781 # some record is forbidden or missing, retry record by record
782 for record in records:
784 self._compute_value(record)
785 except Exception as exc:
786 record._cache[self.name] = FailedValue(exc)
788 def determine_value(self, record):
789 """ Determine the value of `self` for `record`. """
792 if self.store and not (self.depends and env.in_draft):
793 # this is a stored field
795 # this is a stored computed field, check for recomputation
796 recs = record._recompute_check(self)
798 # recompute the value (only in cache)
799 self.compute_value(recs)
800 # HACK: if result is in the wrong cache, copy values
802 for source, target in zip(recs, recs.with_env(env)):
804 values = target._convert_to_cache({
805 f.name: source[f.name] for f in self.computed_fields
807 except MissingError as e:
808 values = FailedValue(e)
809 target._cache.update(values)
810 # the result is saved to database by BaseModel.recompute()
813 # read the field from database
814 record._prefetch_field(self)
817 # this is either a non-stored computed field, or a stored computed
818 # field in draft mode
820 self.compute_value(record)
822 recs = record._in_cache_without(self)
823 self.compute_value(recs)
826 # this is a non-stored non-computed field
827 record._cache[self] = self.null(env)
829 def determine_default(self, record):
830 """ determine the default value of field `self` on `record` """
832 value = self.default(record)
833 record._cache[self] = self.convert_to_cache(value, record)
835 self._compute_value(record)
837 record._cache[self] = SpecialValue(self.null(record.env))
839 def determine_inverse(self, records):
840 """ Given the value of `self` on `records`, inverse the computation. """
842 self.inverse(records)
844 def determine_domain(self, records, operator, value):
845 """ Return a domain representing a condition on `self`. """
847 return self.search(records, operator, value)
849 return [(self.name, operator, value)]
851 ############################################################################
853 # Notification when fields are modified
856 def modified(self, records):
857 """ Notify that field `self` has been modified on `records`: prepare the
858 fields/records to recompute, and return a spec indicating what to
861 # invalidate the fields that depend on self, and prepare recomputation
862 spec = [(self, records._ids)]
863 for field, path in self._triggers:
864 if path and field.store:
865 # don't move this line to function top, see log
866 env = records.env(user=SUPERUSER_ID, context={'active_test': False})
867 target = env[field.model_name].search([(path, 'in', records.ids)])
869 spec.append((field, target._ids))
870 target.with_env(records.env)._recompute_todo(field)
872 spec.append((field, None))
876 def modified_draft(self, records):
877 """ Same as :meth:`modified`, but in draft mode. """
880 # invalidate the fields on the records in cache that depend on
881 # `records`, except fields currently being computed
883 for field, path in self._triggers:
884 target = env[field.model_name]
885 computed = target.browse(env.computed[field])
887 target = records - computed
889 target = (target.browse(env.cache[field]) - computed).filtered(
890 lambda rec: rec._mapped_cache(path) & records
893 target = target.browse(env.cache[field]) - computed
896 spec.append((field, target._ids))
901 class Boolean(Field):
904 def convert_to_cache(self, value, record, validate=True):
907 def convert_to_export(self, value, env):
908 if env.context.get('export_raw_data'):
913 class Integer(Field):
916 def convert_to_cache(self, value, record, validate=True):
917 if isinstance(value, dict):
918 # special case, when an integer field is used as inverse for a one2many
919 return value.get('id', False)
920 return int(value or 0)
922 def convert_to_read(self, value, use_name_get=True):
923 # Integer values greater than 2^31-1 are not supported in pure XMLRPC,
924 # so we have to pass them as floats :-(
925 if value and value > xmlrpclib.MAXINT:
929 def _update(self, records, value):
930 # special case, when an integer field is used as inverse for a one2many
931 records._cache[self] = value.id or 0
935 """ The precision digits are given by the attribute
937 :param digits: a pair (total, decimal), or a function taking a database
938 cursor and returning a pair (total, decimal)
941 _digits = None # digits argument passed to class initializer
942 digits = None # digits as computed by setup()
944 def __init__(self, string=None, digits=None, **kwargs):
945 super(Float, self).__init__(string=string, _digits=digits, **kwargs)
947 def _setup_digits(self, env):
948 """ Setup the digits for `self` and its corresponding column """
949 self.digits = self._digits(env.cr) if callable(self._digits) else self._digits
951 column = env[self.model_name]._columns[self.name]
952 column.digits_change(env.cr)
954 def _setup_regular(self, env):
955 super(Float, self)._setup_regular(env)
956 self._setup_digits(env)
958 _related_digits = property(attrgetter('digits'))
960 _description_digits = property(attrgetter('digits'))
962 _column_digits = property(lambda self: not callable(self._digits) and self._digits)
963 _column_digits_compute = property(lambda self: callable(self._digits) and self._digits)
965 def convert_to_cache(self, value, record, validate=True):
966 # apply rounding here, otherwise value in cache may be wrong!
968 return float_round(float(value or 0.0), precision_digits=self.digits[1])
970 return float(value or 0.0)
973 class _String(Field):
974 """ Abstract class for string fields. """
977 _column_translate = property(attrgetter('translate'))
978 _related_translate = property(attrgetter('translate'))
979 _description_translate = property(attrgetter('translate'))
983 """ Basic string field, can be length-limited, usually displayed as a
984 single-line string in clients
986 :param int size: the maximum size of values stored for that field
987 :param bool translate: whether the values of this field can be translated
992 _column_size = property(attrgetter('size'))
993 _related_size = property(attrgetter('size'))
994 _description_size = property(attrgetter('size'))
996 def convert_to_cache(self, value, record, validate=True):
997 if value is None or value is False:
999 return ustr(value)[:self.size]
1001 class Text(_String):
1002 """ Text field. Very similar to :class:`~.Char` but used for longer
1003 contents and displayed as a multiline text box
1005 :param translate: whether the value of this field can be translated
1009 def convert_to_cache(self, value, record, validate=True):
1010 if value is None or value is False:
1014 class Html(_String):
1016 sanitize = True # whether value must be sanitized
1018 _column_sanitize = property(attrgetter('sanitize'))
1019 _related_sanitize = property(attrgetter('sanitize'))
1020 _description_sanitize = property(attrgetter('sanitize'))
1022 def convert_to_cache(self, value, record, validate=True):
1023 if value is None or value is False:
1025 if validate and self.sanitize:
1026 return html_sanitize(value)
1035 """ Return the current day in the format expected by the ORM.
1036 This function may be used to compute default values.
1038 return date.today().strftime(DATE_FORMAT)
1041 def context_today(record, timestamp=None):
1042 """ Return the current date as seen in the client's timezone in a format
1043 fit for date fields. This method may be used to compute default
1046 :param datetime timestamp: optional datetime value to use instead of
1047 the current date and time (must be a datetime, regular dates
1048 can't be converted between timezones.)
1051 today = timestamp or datetime.now()
1052 context_today = None
1053 tz_name = record._context.get('tz') or record.env.user.tz
1056 today_utc = pytz.timezone('UTC').localize(today, is_dst=False) # UTC = no DST
1057 context_today = today_utc.astimezone(pytz.timezone(tz_name))
1059 _logger.debug("failed to compute context/client-specific today date, using UTC value for `today`",
1061 return (context_today or today).strftime(DATE_FORMAT)
1064 def from_string(value):
1065 """ Convert an ORM `value` into a :class:`date` value. """
1066 value = value[:DATE_LENGTH]
1067 return datetime.strptime(value, DATE_FORMAT).date()
1070 def to_string(value):
1071 """ Convert a :class:`date` value into the format expected by the ORM. """
1072 return value.strftime(DATE_FORMAT)
1074 def convert_to_cache(self, value, record, validate=True):
1077 if isinstance(value, basestring):
1079 # force parsing for validation
1080 self.from_string(value)
1081 return value[:DATE_LENGTH]
1082 return self.to_string(value)
1084 def convert_to_export(self, value, env):
1085 if value and env.context.get('export_raw_data'):
1086 return self.from_string(value)
1087 return bool(value) and ustr(value)
1090 class Datetime(Field):
1095 """ Return the current day and time in the format expected by the ORM.
1096 This function may be used to compute default values.
1098 return datetime.now().strftime(DATETIME_FORMAT)
1101 def context_timestamp(record, timestamp):
1102 """Returns the given timestamp converted to the client's timezone.
1103 This method is *not* meant for use as a _defaults initializer,
1104 because datetime fields are automatically converted upon
1105 display on client side. For _defaults you :meth:`fields.datetime.now`
1106 should be used instead.
1108 :param datetime timestamp: naive datetime value (expressed in UTC)
1109 to be converted to the client timezone
1111 :return: timestamp converted to timezone-aware datetime in context
1114 assert isinstance(timestamp, datetime), 'Datetime instance expected'
1115 tz_name = record._context.get('tz') or record.env.user.tz
1118 utc = pytz.timezone('UTC')
1119 context_tz = pytz.timezone(tz_name)
1120 utc_timestamp = utc.localize(timestamp, is_dst=False) # UTC = no DST
1121 return utc_timestamp.astimezone(context_tz)
1123 _logger.debug("failed to compute context/client-specific timestamp, "
1124 "using the UTC value",
1129 def from_string(value):
1130 """ Convert an ORM `value` into a :class:`datetime` value. """
1131 value = value[:DATETIME_LENGTH]
1132 if len(value) == DATE_LENGTH:
1133 value += " 00:00:00"
1134 return datetime.strptime(value, DATETIME_FORMAT)
1137 def to_string(value):
1138 """ Convert a :class:`datetime` value into the format expected by the ORM. """
1139 return value.strftime(DATETIME_FORMAT)
1141 def convert_to_cache(self, value, record, validate=True):
1144 if isinstance(value, basestring):
1146 # force parsing for validation
1147 self.from_string(value)
1148 value = value[:DATETIME_LENGTH]
1149 if len(value) == DATE_LENGTH:
1150 value += " 00:00:00"
1152 return self.to_string(value)
1154 def convert_to_export(self, value, env):
1155 if value and env.context.get('export_raw_data'):
1156 return self.from_string(value)
1157 return bool(value) and ustr(value)
1160 class Binary(Field):
1164 class Selection(Field):
1166 :param selection: specifies the possible values for this field.
1167 It is given as either a list of pairs (`value`, `string`), or a
1168 model method, or a method name.
1169 :param selection_add: provides an extension of the selection in the case
1170 of an overridden field. It is a list of pairs (`value`, `string`).
1172 The attribute `selection` is mandatory except in the case of
1173 :ref:`related fields <field-related>` or :ref:`field extensions
1174 <field-incremental-definition>`.
1177 selection = None # [(value, string), ...], function or method name
1178 selection_add = None # [(value, string), ...]
1180 def __init__(self, selection=None, string=None, **kwargs):
1181 if callable(selection):
1182 from openerp import api
1183 selection = api.expected(api.model, selection)
1184 super(Selection, self).__init__(selection=selection, string=string, **kwargs)
1186 def _setup_related(self, env):
1187 super(Selection, self)._setup_related(env)
1188 # selection must be computed on related field
1189 field = self.related_field
1190 self.selection = lambda model: field._description_selection(model.env)
1192 def set_class_name(self, cls, name):
1193 super(Selection, self).set_class_name(cls, name)
1194 # determine selection (applying 'selection_add' extensions)
1196 for field in resolve_all_mro(cls, name, reverse=True):
1197 if isinstance(field, type(self)):
1198 # We cannot use field.selection or field.selection_add here
1199 # because those attributes are overridden by `set_class_name`.
1200 if 'selection' in field._attrs:
1201 selection = field._attrs['selection']
1202 if 'selection_add' in field._attrs:
1203 selection = selection + field._attrs['selection_add']
1206 self.selection = selection
1208 def _description_selection(self, env):
1209 """ return the selection list (pairs (value, label)); labels are
1210 translated according to context language
1212 selection = self.selection
1213 if isinstance(selection, basestring):
1214 return getattr(env[self.model_name], selection)()
1215 if callable(selection):
1216 return selection(env[self.model_name])
1218 # translate selection labels
1220 name = "%s,%s" % (self.model_name, self.name)
1221 translate = partial(
1222 env['ir.translation']._get_source, name, 'selection', env.lang)
1223 return [(value, translate(label)) for value, label in selection]
1228 def _column_selection(self):
1229 if isinstance(self.selection, basestring):
1230 method = self.selection
1231 return lambda self, *a, **kw: getattr(self, method)(*a, **kw)
1233 return self.selection
1235 def get_values(self, env):
1236 """ return a list of the possible values """
1237 selection = self.selection
1238 if isinstance(selection, basestring):
1239 selection = getattr(env[self.model_name], selection)()
1240 elif callable(selection):
1241 selection = selection(env[self.model_name])
1242 return [value for value, _ in selection]
1244 def convert_to_cache(self, value, record, validate=True):
1246 return value or False
1247 if value in self.get_values(record.env):
1251 raise ValueError("Wrong value for %s: %r" % (self, value))
1253 def convert_to_export(self, value, env):
1254 if not isinstance(self.selection, list):
1255 # FIXME: this reproduces an existing buggy behavior!
1257 for item in self._description_selection(env):
1258 if item[0] == value:
1263 class Reference(Selection):
1267 def __init__(self, selection=None, string=None, **kwargs):
1268 super(Reference, self).__init__(selection=selection, string=string, **kwargs)
1270 _related_size = property(attrgetter('size'))
1272 _column_size = property(attrgetter('size'))
1274 def convert_to_cache(self, value, record, validate=True):
1275 if isinstance(value, BaseModel):
1276 if ((not validate or value._name in self.get_values(record.env))
1277 and len(value) <= 1):
1278 return value.with_env(record.env) or False
1279 elif isinstance(value, basestring):
1280 res_model, res_id = value.split(',')
1281 return record.env[res_model].browse(int(res_id))
1284 raise ValueError("Wrong value for %s: %r" % (self, value))
1286 def convert_to_read(self, value, use_name_get=True):
1287 return "%s,%s" % (value._name, value.id) if value else False
1289 def convert_to_export(self, value, env):
1290 return bool(value) and value.name_get()[0][1]
1292 def convert_to_display_name(self, value):
1293 return ustr(value and value.display_name)
1296 class _Relational(Field):
1297 """ Abstract class for relational fields. """
1299 domain = None # domain for searching values
1300 context = None # context for searching values
1302 _description_relation = property(attrgetter('comodel_name'))
1303 _description_context = property(attrgetter('context'))
1305 def _description_domain(self, env):
1306 return self.domain(env[self.model_name]) if callable(self.domain) else self.domain
1308 _column_obj = property(attrgetter('comodel_name'))
1309 _column_domain = property(attrgetter('domain'))
1310 _column_context = property(attrgetter('context'))
1312 def null(self, env):
1313 return env[self.comodel_name]
1315 def modified(self, records):
1316 # Invalidate cache for self.inverse_fields, too. Note that recomputation
1317 # of fields that depend on self.inverse_fields is already covered by the
1318 # triggers (see above).
1319 spec = super(_Relational, self).modified(records)
1320 for invf in self.inverse_fields:
1321 spec.append((invf, None))
1325 class Many2one(_Relational):
1326 """ The value of such a field is a recordset of size 0 (no
1327 record) or 1 (a single record).
1329 :param comodel_name: name of the target model (string)
1331 :param domain: an optional domain to set on candidate values on the
1332 client side (domain or string)
1334 :param context: an optional context to use on the client side when
1335 handling that field (dictionary)
1337 :param ondelete: what to do when the referred record is deleted;
1338 possible values are: ``'set null'``, ``'restrict'``, ``'cascade'``
1340 :param auto_join: whether JOINs are generated upon search through that
1341 field (boolean, by default ``False``)
1343 :param delegate: set it to ``True`` to make fields of the target model
1344 accessible from the current model (corresponds to ``_inherits``)
1346 The attribute `comodel_name` is mandatory except in the case of related
1347 fields or field extensions.
1350 ondelete = 'set null' # what to do when value is deleted
1351 auto_join = False # whether joins are generated upon search
1352 delegate = False # whether self implements delegation
1354 def __init__(self, comodel_name=None, string=None, **kwargs):
1355 super(Many2one, self).__init__(comodel_name=comodel_name, string=string, **kwargs)
1357 def set_class_name(self, cls, name):
1358 super(Many2one, self).set_class_name(cls, name)
1359 # determine self.delegate
1360 if not self.delegate:
1361 self.delegate = name in cls._inherits.values()
1363 _column_ondelete = property(attrgetter('ondelete'))
1364 _column_auto_join = property(attrgetter('auto_join'))
1366 def _update(self, records, value):
1367 """ Update the cached value of `self` for `records` with `value`. """
1368 records._cache[self] = value
1370 def convert_to_cache(self, value, record, validate=True):
1371 if isinstance(value, (NoneType, int)):
1372 return record.env[self.comodel_name].browse(value)
1373 if isinstance(value, BaseModel):
1374 if value._name == self.comodel_name and len(value) <= 1:
1375 return value.with_env(record.env)
1376 raise ValueError("Wrong value for %s: %r" % (self, value))
1377 elif isinstance(value, tuple):
1378 return record.env[self.comodel_name].browse(value[0])
1379 elif isinstance(value, dict):
1380 return record.env[self.comodel_name].new(value)
1382 return record.env[self.comodel_name].browse(value)
1384 def convert_to_read(self, value, use_name_get=True):
1385 if use_name_get and value:
1386 # evaluate name_get() as superuser, because the visibility of a
1387 # many2one field value (id and name) depends on the current record's
1388 # access rights, and not the value's access rights.
1390 return value.sudo().name_get()[0]
1391 except MissingError:
1392 # Should not happen, unless the foreign key is missing.
1397 def convert_to_write(self, value, target=None, fnames=None):
1400 def convert_to_onchange(self, value):
1403 def convert_to_export(self, value, env):
1404 return bool(value) and value.name_get()[0][1]
1406 def convert_to_display_name(self, value):
1407 return ustr(value.display_name)
1409 def determine_default(self, record):
1410 super(Many2one, self).determine_default(record)
1412 # special case: fields that implement inheritance between models
1413 value = record[self.name]
1415 # the default value cannot be null, use a new record instead
1416 record[self.name] = record.env[self.comodel_name].new()
1419 class UnionUpdate(SpecialValue):
1420 """ Placeholder for a value update; when this value is taken from the cache,
1421 it returns ``record[field.name] | value`` and stores it in the cache.
1423 def __init__(self, field, record, value):
1424 self.args = (field, record, value)
1427 field, record, value = self.args
1428 # in order to read the current field's value, remove self from cache
1429 del record._cache[field]
1430 # read the current field's value, and update it in cache only
1431 record._cache[field] = new_value = record[field.name] | value
1435 class _RelationalMulti(_Relational):
1436 """ Abstract class for relational fields *2many. """
1438 def _update(self, records, value):
1439 """ Update the cached value of `self` for `records` with `value`. """
1440 for record in records:
1441 if self in record._cache:
1442 record._cache[self] = record[self.name] | value
1444 record._cache[self] = UnionUpdate(self, record, value)
1446 def convert_to_cache(self, value, record, validate=True):
1447 if isinstance(value, BaseModel):
1448 if value._name == self.comodel_name:
1449 return value.with_env(record.env)
1450 elif isinstance(value, list):
1451 # value is a list of record ids or commands
1453 record = record.browse() # new record has no value
1454 result = record[self.name]
1455 # modify result with the commands;
1456 # beware to not introduce duplicates in result
1457 for command in value:
1458 if isinstance(command, (tuple, list)):
1460 result += result.new(command[2])
1461 elif command[0] == 1:
1462 result.browse(command[1]).update(command[2])
1463 result += result.browse(command[1]) - result
1464 elif command[0] == 2:
1465 # note: the record will be deleted by write()
1466 result -= result.browse(command[1])
1467 elif command[0] == 3:
1468 result -= result.browse(command[1])
1469 elif command[0] == 4:
1470 result += result.browse(command[1]) - result
1471 elif command[0] == 5:
1472 result = result.browse()
1473 elif command[0] == 6:
1474 result = result.browse(command[2])
1475 elif isinstance(command, dict):
1476 result += result.new(command)
1478 result += result.browse(command) - result
1481 return self.null(record.env)
1482 raise ValueError("Wrong value for %s: %s" % (self, value))
1484 def convert_to_read(self, value, use_name_get=True):
1487 def convert_to_write(self, value, target=None, fnames=None):
1488 # remove/delete former records
1491 result = [(6, 0, set_ids)]
1492 add_existing = lambda id: set_ids.append(id)
1494 tag = 2 if self.type == 'one2many' else 3
1495 result = [(tag, record.id) for record in target[self.name] - value]
1496 add_existing = lambda id: result.append((4, id))
1499 # take all fields in cache, except the inverses of self
1500 fnames = set(value._fields) - set(MAGIC_COLUMNS)
1501 for invf in self.inverse_fields:
1502 fnames.discard(invf.name)
1504 # add new and existing records
1505 for record in value:
1506 if not record.id or record._dirty:
1507 values = dict((k, v) for k, v in record._cache.iteritems() if k in fnames)
1508 values = record._convert_to_write(values)
1510 result.append((0, 0, values))
1512 result.append((1, record.id, values))
1514 add_existing(record.id)
1518 def convert_to_export(self, value, env):
1519 return bool(value) and ','.join(name for id, name in value.name_get())
1521 def convert_to_display_name(self, value):
1522 raise NotImplementedError()
1524 def _compute_related(self, records):
1525 """ Compute the related field `self` on `records`. """
1526 for record in records:
1528 # traverse the intermediate fields, and keep at most one record
1529 for name in self.related[:-1]:
1530 value = value[name][:1]
1531 record[self.name] = value[self.related[-1]]
1534 class One2many(_RelationalMulti):
1535 """ One2many field; the value of such a field is the recordset of all the
1536 records in `comodel_name` such that the field `inverse_name` is equal to
1539 :param comodel_name: name of the target model (string)
1541 :param inverse_name: name of the inverse `Many2one` field in
1542 `comodel_name` (string)
1544 :param domain: an optional domain to set on candidate values on the
1545 client side (domain or string)
1547 :param context: an optional context to use on the client side when
1548 handling that field (dictionary)
1550 :param auto_join: whether JOINs are generated upon search through that
1551 field (boolean, by default ``False``)
1553 :param limit: optional limit to use upon read (integer)
1555 The attributes `comodel_name` and `inverse_name` are mandatory except in
1556 the case of related fields or field extensions.
1559 inverse_name = None # name of the inverse field
1560 auto_join = False # whether joins are generated upon search
1561 limit = None # optional limit to use upon read
1562 copyable = False # o2m are not copied by default
1564 def __init__(self, comodel_name=None, inverse_name=None, string=None, **kwargs):
1565 super(One2many, self).__init__(
1566 comodel_name=comodel_name,
1567 inverse_name=inverse_name,
1572 def _setup_regular(self, env):
1573 super(One2many, self)._setup_regular(env)
1575 if self.inverse_name:
1576 # link self to its inverse field and vice-versa
1577 invf = env[self.comodel_name]._fields[self.inverse_name]
1578 # In some rare cases, a `One2many` field can link to `Int` field
1579 # (res_model/res_id pattern). Only inverse the field if this is
1580 # a `Many2one` field.
1581 if isinstance(invf, Many2one):
1582 self.inverse_fields.append(invf)
1583 invf.inverse_fields.append(self)
1585 _description_relation_field = property(attrgetter('inverse_name'))
1587 _column_fields_id = property(attrgetter('inverse_name'))
1588 _column_auto_join = property(attrgetter('auto_join'))
1589 _column_limit = property(attrgetter('limit'))
1592 class Many2many(_RelationalMulti):
1593 """ Many2many field; the value of such a field is the recordset.
1595 :param comodel_name: name of the target model (string)
1597 The attribute `comodel_name` is mandatory except in the case of related
1598 fields or field extensions.
1600 :param relation: optional name of the table that stores the relation in
1601 the database (string)
1603 :param column1: optional name of the column referring to "these" records
1604 in the table `relation` (string)
1606 :param column2: optional name of the column referring to "those" records
1607 in the table `relation` (string)
1609 The attributes `relation`, `column1` and `column2` are optional. If not
1610 given, names are automatically generated from model names, provided
1611 `model_name` and `comodel_name` are different!
1613 :param domain: an optional domain to set on candidate values on the
1614 client side (domain or string)
1616 :param context: an optional context to use on the client side when
1617 handling that field (dictionary)
1619 :param limit: optional limit to use upon read (integer)
1623 relation = None # name of table
1624 column1 = None # column of table referring to model
1625 column2 = None # column of table referring to comodel
1626 limit = None # optional limit to use upon read
1628 def __init__(self, comodel_name=None, relation=None, column1=None, column2=None,
1629 string=None, **kwargs):
1630 super(Many2many, self).__init__(
1631 comodel_name=comodel_name,
1639 def _setup_regular(self, env):
1640 super(Many2many, self)._setup_regular(env)
1642 if self.store and not self.relation:
1643 model = env[self.model_name]
1644 column = model._columns[self.name]
1645 if not isinstance(column, fields.function):
1646 self.relation, self.column1, self.column2 = column._sql_names(model)
1649 m2m = env.registry._m2m
1650 # if inverse field has already been setup, it is present in m2m
1651 invf = m2m.get((self.relation, self.column2, self.column1))
1653 self.inverse_fields.append(invf)
1654 invf.inverse_fields.append(self)
1656 # add self in m2m, so that its inverse field can find it
1657 m2m[(self.relation, self.column1, self.column2)] = self
1659 _column_rel = property(attrgetter('relation'))
1660 _column_id1 = property(attrgetter('column1'))
1661 _column_id2 = property(attrgetter('column2'))
1662 _column_limit = property(attrgetter('limit'))
1666 """ Special case for field 'id'. """
1668 #: Can't write this!
1671 def __init__(self, string=None, **kwargs):
1672 super(Id, self).__init__(type='integer', string=string, **kwargs)
1674 def to_column(self):
1675 """ to_column() -> fields._column
1679 return fields.integer('ID')
1681 def __get__(self, record, owner):
1683 return self # the field is accessed through the class owner
1686 return record.ensure_one()._ids[0]
1688 def __set__(self, record, value):
1689 raise TypeError("field 'id' cannot be assigned")
1692 # imported here to avoid dependency cycle issues
1693 from openerp import SUPERUSER_ID
1694 from .exceptions import Warning, AccessError, MissingError
1695 from .models import BaseModel, MAGIC_COLUMNS
1696 from .osv import fields