1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2013-2014 OpenERP (<http://www.openerp.com>).
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as
9 # published by the Free Software Foundation, either version 3 of the
10 # License, or (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 ##############################################################################
22 """ High-level objects for fields. """
25 from datetime import date, datetime
26 from functools import partial
27 from operator import attrgetter
32 from types import NoneType
34 from openerp.tools import float_round, ustr, html_sanitize
35 from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as DATE_FORMAT
36 from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT as DATETIME_FORMAT
38 DATE_LENGTH = len(date.today().strftime(DATE_FORMAT))
39 DATETIME_LENGTH = len(datetime.now().strftime(DATETIME_FORMAT))
41 _logger = logging.getLogger(__name__)
43 class SpecialValue(object):
44 """ Encapsulates a value in the cache in place of a normal value. """
45 def __init__(self, value):
50 class FailedValue(SpecialValue):
51 """ Special value that encapsulates an exception instead of a value. """
52 def __init__(self, exception):
53 self.exception = exception
57 def _check_value(value):
58 """ Return `value`, or call its getter if `value` is a :class:`SpecialValue`. """
59 return value.get() if isinstance(value, SpecialValue) else value
62 def resolve_all_mro(cls, name, reverse=False):
63 """ Return the (successively overridden) values of attribute `name` in `cls`
64 in mro order, or inverse mro order if `reverse` is true.
66 klasses = reversed(cls.__mro__) if reverse else cls.__mro__
68 if name in klass.__dict__:
69 yield klass.__dict__[name]
72 def default_compute(field, value):
73 """ Return a compute function for the given default `value`; `value` is
74 either a constant, or a unary function returning the default value.
77 func = value if callable(value) else lambda rec: value
84 class MetaField(type):
85 """ Metaclass for field classes. """
88 def __init__(cls, name, bases, attrs):
89 super(MetaField, cls).__init__(name, bases, attrs)
91 cls.by_type[cls.type] = cls
93 # compute class attributes to avoid calling dir() on fields
95 cls.related_attrs = []
96 cls.description_attrs = []
98 if attr.startswith('_column_'):
99 cls.column_attrs.append((attr[8:], attr))
100 elif attr.startswith('_related_'):
101 cls.related_attrs.append((attr[9:], attr))
102 elif attr.startswith('_description_'):
103 cls.description_attrs.append((attr[13:], attr))
107 """ The field descriptor contains the field definition, and manages accesses
108 and assignments of the corresponding field on records. The following
109 attributes may be provided when instanciating a field:
111 :param string: the label of the field seen by users (string); if not
112 set, the ORM takes the field name in the class (capitalized).
114 :param help: the tooltip of the field seen by users (string)
116 :param readonly: whether the field is readonly (boolean, by default ``False``)
118 :param required: whether the value of the field is required (boolean, by
121 :param index: whether the field is indexed in database (boolean, by
124 :param default: the default value for the field; this is either a static
125 value, or a function taking a recordset and returning a value
127 :param states: a dictionary mapping state values to lists of UI attribute-value
128 pairs; possible attributes are: 'readonly', 'required', 'invisible'.
129 Note: Any state-based condition requires the ``state`` field value to be
130 available on the client-side UI. This is typically done by including it in
131 the relevant views, possibly made invisible if not relevant for the
134 :param groups: comma-separated list of group xml ids (string); this
135 restricts the field access to the users of the given groups only
137 :param bool copy: whether the field value should be copied when the record
138 is duplicated (default: ``True`` for normal fields, ``False`` for
139 ``one2many`` and computed fields, including property fields and
144 .. rubric:: Computed fields
146 One can define a field whose value is computed instead of simply being
147 read from the database. The attributes that are specific to computed
148 fields are given below. To define such a field, simply provide a value
149 for the attribute `compute`.
151 :param compute: name of a method that computes the field
153 :param inverse: name of a method that inverses the field (optional)
155 :param search: name of a method that implement search on the field (optional)
157 :param store: whether the field is stored in database (boolean, by
158 default ``False`` on computed fields)
160 The methods given for `compute`, `inverse` and `search` are model
161 methods. Their signature is shown in the following example::
163 upper = fields.Char(compute='_compute_upper',
164 inverse='_inverse_upper',
165 search='_search_upper')
168 def _compute_upper(self):
170 self.upper = self.name.upper() if self.name else False
172 def _inverse_upper(self):
174 self.name = self.upper.lower() if self.upper else False
176 def _search_upper(self, operator, value):
177 if operator == 'like':
179 return [('name', operator, value)]
181 The compute method has to assign the field on all records of the invoked
182 recordset. The decorator :meth:`openerp.api.depends` must be applied on
183 the compute method to specify the field dependencies; those dependencies
184 are used to determine when to recompute the field; recomputation is
185 automatic and guarantees cache/database consistency. Note that the same
186 method can be used for several fields, you simply have to assign all the
187 given fields in the method; the method will be invoked once for all
190 By default, a computed field is not stored to the database, and is
191 computed on-the-fly. Adding the attribute ``store=True`` will store the
192 field's values in the database. The advantage of a stored field is that
193 searching on that field is done by the database itself. The disadvantage
194 is that it requires database updates when the field must be recomputed.
196 The inverse method, as its name says, does the inverse of the compute
197 method: the invoked records have a value for the field, and you must
198 apply the necessary changes on the field dependencies such that the
199 computation gives the expected value. Note that a computed field without
200 an inverse method is readonly by default.
202 The search method is invoked when processing domains before doing an
203 actual search on the model. It must return a domain equivalent to the
204 condition: `field operator value`.
208 .. rubric:: Related fields
210 The value of a related field is given by following a sequence of
211 relational fields and reading a field on the reached model. The complete
212 sequence of fields to traverse is specified by the attribute
214 :param related: sequence of field names
216 The value of some attributes from related fields are automatically taken
217 from the source field, when it makes sense. Examples are the attributes
218 `string` or `selection` on selection fields.
220 By default, the values of related fields are not stored to the database.
221 Add the attribute ``store=True`` to make it stored, just like computed
222 fields. Related fields are automatically recomputed when their
223 dependencies are modified.
225 .. _field-company-dependent:
227 .. rubric:: Company-dependent fields
229 Formerly known as 'property' fields, the value of those fields depends
230 on the company. In other words, users that belong to different companies
231 may see different values for the field on a given record.
233 :param company_dependent: whether the field is company-dependent (boolean)
235 .. _field-incremental-definition:
237 .. rubric:: Incremental definition
239 A field is defined as class attribute on a model class. If the model
240 is extended (see :class:`~openerp.models.Model`), one can also extend
241 the field definition by redefining a field with the same name and same
242 type on the subclass. In that case, the attributes of the field are
243 taken from the parent class and overridden by the ones given in
246 For instance, the second class below only adds a tooltip on the field
249 class First(models.Model):
251 state = fields.Selection([...], required=True)
253 class Second(models.Model):
255 state = fields.Selection(help="Blah blah blah")
258 __metaclass__ = MetaField
260 _attrs = None # dictionary with all field attributes
261 _free_attrs = None # list of semantic-free attribute names
263 automatic = False # whether the field is automatically created ("magic" field)
264 _origin = None # the column or field interfaced by self, if any
266 name = None # name of the field
267 type = None # type of the field (string)
268 relational = False # whether the field is a relational one
269 model_name = None # name of the model of this field
270 comodel_name = None # name of the model of values (if relational)
271 inverse_fields = None # list of inverse fields (objects)
273 store = True # whether the field is stored in database
274 index = False # whether the field is indexed in database
275 manual = False # whether the field is a custom field
276 copyable = True # whether the field is copied over by BaseModel.copy()
277 depends = () # collection of field dependencies
278 recursive = False # whether self depends on itself
279 compute = None # compute(recs) computes field on recs
280 inverse = None # inverse(recs) inverses field on recs
281 search = None # search(recs, operator, value) searches on self
282 related = None # sequence of field names, for related fields
283 related_sudo = True # whether related fields should be read as admin
284 company_dependent = False # whether `self` is company-dependent (property field)
285 default = None # default value
287 string = None # field label
288 help = None # field tooltip
292 groups = False # csv list of group xml ids
293 change_default = None # whether the field may trigger a "user-onchange"
294 deprecated = None # whether the field is ... deprecated
296 def __init__(self, string=None, **kwargs):
297 kwargs['string'] = string
298 self._attrs = {key: val for key, val in kwargs.iteritems() if val is not None}
299 self._free_attrs = []
301 def copy(self, **kwargs):
302 """ copy(item) -> test
304 make a copy of `self`, possibly modified with parameters `kwargs` """
306 field._attrs = {key: val for key, val in kwargs.iteritems() if val is not None}
307 field._free_attrs = list(self._free_attrs)
310 def set_class_name(self, cls, name):
311 """ Assign the model class and field name of `self`. """
312 self.model_name = cls._name
315 # determine all inherited field attributes
317 for field in resolve_all_mro(cls, name, reverse=True):
318 if isinstance(field, type(self)):
319 attrs.update(field._attrs)
322 attrs.update(self._attrs) # necessary in case self is not in cls
324 # initialize `self` with `attrs`
325 if attrs.get('compute'):
326 # by default, computed fields are not stored, not copied and readonly
327 attrs['store'] = attrs.get('store', False)
328 attrs['copy'] = attrs.get('copy', False)
329 attrs['readonly'] = attrs.get('readonly', not attrs.get('inverse'))
330 if attrs.get('related'):
331 # by default, related fields are not stored
332 attrs['store'] = attrs.get('store', False)
334 # attribute is copyable because there is also a copy() method
335 attrs['copyable'] = attrs.pop('copy')
337 for attr, value in attrs.iteritems():
338 if not hasattr(self, attr):
339 self._free_attrs.append(attr)
340 setattr(self, attr, value)
343 self.string = name.replace('_', ' ').capitalize()
348 return "%s.%s" % (self.model_name, self.name)
351 return "%s.%s" % (self.model_name, self.name)
353 ############################################################################
359 """ Prepare `self` for a new setup. """
360 self._setup_done = False
361 # self._triggers is a set of pairs (field, path) that represents the
362 # computed fields that depend on `self`. When `self` is modified, it
363 # invalidates the cache of each `field`, and registers the records to
364 # recompute based on `path`. See method `modified` below for details.
365 self._triggers = set()
366 self.inverse_fields = []
368 def setup(self, env):
369 """ Complete the setup of `self` (dependencies, recomputation triggers,
370 and other properties). This method is idempotent: it has no effect
371 if `self` has already been set up.
373 if not self._setup_done:
374 self._setup_done = True
377 def _setup(self, env):
378 """ Do the actual setup of `self`. """
380 self._setup_related(env)
382 self._setup_regular(env)
384 # put invalidation/recomputation triggers on field dependencies
385 model = env[self.model_name]
386 for path in self.depends:
387 self._setup_dependency([], model, path.split('.'))
389 # put invalidation triggers on model dependencies
390 for dep_model_name, field_names in model._depends.iteritems():
391 dep_model = env[dep_model_name]
392 for field_name in field_names:
393 field = dep_model._fields[field_name]
394 field._triggers.add((self, None))
397 # Setup of related fields
400 def _setup_related(self, env):
401 """ Setup the attributes of a related field. """
402 # fix the type of self.related if necessary
403 if isinstance(self.related, basestring):
404 self.related = tuple(self.related.split('.'))
406 # determine the related field, and make sure it is set up
407 recs = env[self.model_name]
408 for name in self.related[:-1]:
410 field = self.related_field = recs._fields[self.related[-1]]
413 # check type consistency
414 if self.type != field.type:
415 raise Warning("Type of related field %s is inconsistent with %s" % (self, field))
417 # determine dependencies, compute, inverse, and search
418 self.depends = ('.'.join(self.related),)
419 self.compute = self._compute_related
420 self.inverse = self._inverse_related
421 if field._description_searchable(env):
422 self.search = self._search_related
424 # copy attributes from field to self (string, help, etc.)
425 for attr, prop in self.related_attrs:
426 if not getattr(self, attr):
427 setattr(self, attr, getattr(field, prop))
429 def _compute_related(self, records):
430 """ Compute the related field `self` on `records`. """
431 # when related_sudo, bypass access rights checks when reading values
432 others = records.sudo() if self.related_sudo else records
433 for record, other in zip(records, others):
435 # draft record, do not switch to another environment
437 # traverse the intermediate fields; follow the first record at each step
438 for name in self.related[:-1]:
439 other = other[name][:1]
440 record[self.name] = other[self.related[-1]]
442 def _inverse_related(self, records):
443 """ Inverse the related field `self` on `records`. """
444 for record in records:
446 # traverse the intermediate fields, and keep at most one record
447 for name in self.related[:-1]:
448 other = other[name][:1]
450 other[self.related[-1]] = record[self.name]
452 def _search_related(self, records, operator, value):
453 """ Determine the domain to search on field `self`. """
454 return [('.'.join(self.related), operator, value)]
456 # properties used by _setup_related() to copy values from related field
457 _related_string = property(attrgetter('string'))
458 _related_help = property(attrgetter('help'))
459 _related_readonly = property(attrgetter('readonly'))
460 _related_groups = property(attrgetter('groups'))
463 # Setup of non-related fields
466 def _setup_regular(self, env):
467 """ Setup the attributes of a non-related field. """
468 recs = env[self.model_name]
470 def make_depends(deps):
471 return tuple(deps(recs) if callable(deps) else deps)
473 # transform self.default into self.compute
474 if self.default is not None and self.compute is None:
475 self.compute = default_compute(self, self.default)
477 # convert compute into a callable and determine depends
478 if isinstance(self.compute, basestring):
479 # if the compute method has been overridden, concatenate all their _depends
481 for method in resolve_all_mro(type(recs), self.compute, reverse=True):
482 self.depends += make_depends(getattr(method, '_depends', ()))
483 self.compute = getattr(type(recs), self.compute)
485 self.depends = make_depends(getattr(self.compute, '_depends', ()))
487 # convert inverse and search into callables
488 if isinstance(self.inverse, basestring):
489 self.inverse = getattr(type(recs), self.inverse)
490 if isinstance(self.search, basestring):
491 self.search = getattr(type(recs), self.search)
493 def _setup_dependency(self, path0, model, path1):
494 """ Make `self` depend on `model`; `path0 + path1` is a dependency of
495 `self`, and `path0` is the sequence of field names from `self.model`
499 head, tail = path1[0], path1[1:]
502 # special case: add triggers on all fields of model (except self)
503 fields = set(model._fields.itervalues()) - set([self])
505 fields = [model._fields[head]]
509 _logger.debug("Field %s is recursively defined", self)
510 self.recursive = True
515 #_logger.debug("Add trigger on %s to recompute %s", field, self)
516 field._triggers.add((self, '.'.join(path0 or ['id'])))
518 # add trigger on inverse fields, too
519 for invf in field.inverse_fields:
520 #_logger.debug("Add trigger on %s to recompute %s", invf, self)
521 invf._triggers.add((self, '.'.join(path0 + [head])))
523 # recursively traverse the dependency
525 comodel = env[field.comodel_name]
526 self._setup_dependency(path0 + [head], comodel, tail)
529 def dependents(self):
530 """ Return the computed fields that depend on `self`. """
531 return (field for field, path in self._triggers)
533 ############################################################################
538 def get_description(self, env):
539 """ Return a dictionary that describes the field `self`. """
540 desc = {'type': self.type}
541 for attr, prop in self.description_attrs:
542 value = getattr(self, prop)
545 if value is not None:
550 # properties used by get_description()
552 def _description_store(self, env):
554 # if the corresponding column is a function field, check the column
555 column = env[self.model_name]._columns.get(self.name)
556 return bool(getattr(column, 'store', True))
559 def _description_searchable(self, env):
560 return self._description_store(env) or bool(self.search)
562 _description_manual = property(attrgetter('manual'))
563 _description_depends = property(attrgetter('depends'))
564 _description_related = property(attrgetter('related'))
565 _description_company_dependent = property(attrgetter('company_dependent'))
566 _description_readonly = property(attrgetter('readonly'))
567 _description_required = property(attrgetter('required'))
568 _description_states = property(attrgetter('states'))
569 _description_groups = property(attrgetter('groups'))
570 _description_change_default = property(attrgetter('change_default'))
571 _description_deprecated = property(attrgetter('deprecated'))
573 def _description_string(self, env):
574 if self.string and env.lang:
575 name = "%s,%s" % (self.model_name, self.name)
576 trans = env['ir.translation']._get_source(name, 'field', env.lang)
577 return trans or self.string
580 def _description_help(self, env):
581 if self.help and env.lang:
582 name = "%s,%s" % (self.model_name, self.name)
583 trans = env['ir.translation']._get_source(name, 'help', env.lang)
584 return trans or self.help
587 ############################################################################
589 # Conversion to column instance
593 """ return a low-level field object corresponding to `self` """
596 assert isinstance(self._origin, fields._column)
599 _logger.debug("Create fields._column for Field %s", self)
601 for attr, prop in self.column_attrs:
602 args[attr] = getattr(self, prop)
603 for attr in self._free_attrs:
604 args[attr] = getattr(self, attr)
606 if self.company_dependent:
607 # company-dependent fields are mapped to former property fields
608 args['type'] = self.type
609 args['relation'] = self.comodel_name
610 return fields.property(**args)
612 return getattr(fields, self.type)(**args)
614 # properties used by to_column() to create a column instance
615 _column_copy = property(attrgetter('copyable'))
616 _column_select = property(attrgetter('index'))
617 _column_manual = property(attrgetter('manual'))
618 _column_string = property(attrgetter('string'))
619 _column_help = property(attrgetter('help'))
620 _column_readonly = property(attrgetter('readonly'))
621 _column_required = property(attrgetter('required'))
622 _column_states = property(attrgetter('states'))
623 _column_groups = property(attrgetter('groups'))
624 _column_change_default = property(attrgetter('change_default'))
625 _column_deprecated = property(attrgetter('deprecated'))
627 ############################################################################
629 # Conversion of values
633 """ return the null value for this field in the given environment """
636 def convert_to_cache(self, value, record, validate=True):
637 """ convert `value` to the cache level in `env`; `value` may come from
638 an assignment, or have the format of methods :meth:`BaseModel.read`
639 or :meth:`BaseModel.write`
641 :param record: the target record for the assignment, or an empty recordset
643 :param bool validate: when True, field-specific validation of
644 `value` will be performed
648 def convert_to_read(self, value, use_name_get=True):
649 """ convert `value` from the cache to a value as returned by method
650 :meth:`BaseModel.read`
652 :param bool use_name_get: when True, value's diplay name will
653 be computed using :meth:`BaseModel.name_get`, if relevant
656 return False if value is None else value
658 def convert_to_write(self, value, target=None, fnames=None):
659 """ convert `value` from the cache to a valid value for method
660 :meth:`BaseModel.write`.
662 :param target: optional, the record to be modified with this value
663 :param fnames: for relational fields only, an optional collection of
664 field names to convert
666 return self.convert_to_read(value)
668 def convert_to_onchange(self, value):
669 """ convert `value` from the cache to a valid value for an onchange
672 return self.convert_to_write(value)
674 def convert_to_export(self, value, env):
675 """ convert `value` from the cache to a valid value for export. The
676 parameter `env` is given for managing translations.
678 if env.context.get('export_raw_data'):
680 return bool(value) and ustr(value)
682 def convert_to_display_name(self, value):
683 """ convert `value` from the cache to a suitable display name. """
686 ############################################################################
691 def __get__(self, record, owner):
692 """ return the value of field `self` on `record` """
694 return self # the field is accessed through the owner class
697 # null record -> return the null value for this field
698 return self.null(record.env)
700 # only a single record may be accessed
704 return record._cache[self]
708 # cache miss, retrieve value
710 # normal record -> read or compute value for this field
711 self.determine_value(record)
713 # new record -> compute default value for this field
714 record.add_default_value(self)
716 # the result should be in cache now
717 return record._cache[self]
719 def __set__(self, record, value):
720 """ set the value of field `self` on `record` """
723 # only a single record may be updated
726 # adapt value to the cache level
727 value = self.convert_to_cache(value, record)
729 if env.in_draft or not record.id:
730 # determine dependent fields
731 spec = self.modified_draft(record)
733 # set value in cache, inverse field, and mark record as dirty
734 record._cache[self] = value
736 for invf in self.inverse_fields:
737 invf._update(value, record)
740 # determine more dependent fields, and invalidate them
742 spec += self.modified_draft(record)
746 # simply write to the database, and update cache
747 record.write({self.name: self.convert_to_write(value)})
748 record._cache[self] = value
750 ############################################################################
752 # Computation of field values
755 def _compute_value(self, records):
756 """ Invoke the compute method on `records`. """
757 # mark the computed fields failed in cache, so that access before
758 # computation raises an exception
759 exc = Warning("Field %s is accessed before being computed." % self)
760 for field in self.computed_fields:
761 records._cache[field] = FailedValue(exc)
762 records.env.computed[field].update(records._ids)
763 self.compute(records)
764 for field in self.computed_fields:
765 records.env.computed[field].difference_update(records._ids)
767 def compute_value(self, records):
768 """ Invoke the compute method on `records`; the results are in cache. """
769 with records.env.do_in_draft():
771 self._compute_value(records)
772 except (AccessError, MissingError):
773 # some record is forbidden or missing, retry record by record
774 for record in records:
776 self._compute_value(record)
777 except Exception as exc:
778 record._cache[self.name] = FailedValue(exc)
780 def determine_value(self, record):
781 """ Determine the value of `self` for `record`. """
784 if self.store and not (self.depends and env.in_draft):
785 # this is a stored field
787 # this is a stored computed field, check for recomputation
788 recs = record._recompute_check(self)
790 # recompute the value (only in cache)
791 self.compute_value(recs)
792 # HACK: if result is in the wrong cache, copy values
794 for source, target in zip(recs, recs.with_env(env)):
796 values = target._convert_to_cache({
797 f.name: source[f.name] for f in self.computed_fields
799 except MissingError as e:
800 values = FailedValue(e)
801 target._cache.update(values)
802 # the result is saved to database by BaseModel.recompute()
805 # read the field from database
806 record._prefetch_field(self)
809 # this is either a non-stored computed field, or a stored computed
810 # field in draft mode
812 self.compute_value(record)
814 recs = record._in_cache_without(self)
815 self.compute_value(recs)
818 # this is a non-stored non-computed field
819 record._cache[self] = self.null(env)
821 def determine_default(self, record):
822 """ determine the default value of field `self` on `record` """
824 self._compute_value(record)
826 record._cache[self] = SpecialValue(self.null(record.env))
828 def determine_inverse(self, records):
829 """ Given the value of `self` on `records`, inverse the computation. """
831 self.inverse(records)
833 def determine_domain(self, records, operator, value):
834 """ Return a domain representing a condition on `self`. """
836 return self.search(records, operator, value)
838 return [(self.name, operator, value)]
840 ############################################################################
842 # Notification when fields are modified
845 def modified(self, records):
846 """ Notify that field `self` has been modified on `records`: prepare the
847 fields/records to recompute, and return a spec indicating what to
850 # invalidate the fields that depend on self, and prepare recomputation
851 spec = [(self, records._ids)]
852 for field, path in self._triggers:
853 if path and field.store:
854 # don't move this line to function top, see log
855 env = records.env(user=SUPERUSER_ID, context={'active_test': False})
856 target = env[field.model_name].search([(path, 'in', records.ids)])
858 spec.append((field, target._ids))
859 target.with_env(records.env)._recompute_todo(field)
861 spec.append((field, None))
865 def modified_draft(self, records):
866 """ Same as :meth:`modified`, but in draft mode. """
869 # invalidate the fields on the records in cache that depend on
870 # `records`, except fields currently being computed
872 for field, path in self._triggers:
873 target = env[field.model_name]
874 computed = target.browse(env.computed[field])
876 target = records - computed
878 target = (target.browse(env.cache[field]) - computed).filtered(
879 lambda rec: rec._mapped_cache(path) & records
882 target = target.browse(env.cache[field]) - computed
885 spec.append((field, target._ids))
890 class Boolean(Field):
893 def convert_to_cache(self, value, record, validate=True):
896 def convert_to_export(self, value, env):
897 if env.context.get('export_raw_data'):
902 class Integer(Field):
905 def convert_to_cache(self, value, record, validate=True):
906 if isinstance(value, dict):
907 # special case, when an integer field is used as inverse for a one2many
908 return value.get('id', False)
909 return int(value or 0)
911 def convert_to_read(self, value, use_name_get=True):
912 # Integer values greater than 2^31-1 are not supported in pure XMLRPC,
913 # so we have to pass them as floats :-(
914 if value and value > xmlrpclib.MAXINT:
918 def _update(self, records, value):
919 # special case, when an integer field is used as inverse for a one2many
920 records._cache[self] = value.id or 0
924 """ The precision digits are given by the attribute
926 :param digits: a pair (total, decimal), or a function taking a database
927 cursor and returning a pair (total, decimal)
930 _digits = None # digits argument passed to class initializer
931 digits = None # digits as computed by setup()
933 def __init__(self, string=None, digits=None, **kwargs):
934 super(Float, self).__init__(string=string, _digits=digits, **kwargs)
936 def _setup_digits(self, env):
937 """ Setup the digits for `self` and its corresponding column """
938 self.digits = self._digits(env.cr) if callable(self._digits) else self._digits
940 column = env[self.model_name]._columns[self.name]
941 column.digits_change(env.cr)
943 def _setup_regular(self, env):
944 super(Float, self)._setup_regular(env)
945 self._setup_digits(env)
947 _related_digits = property(attrgetter('digits'))
949 _description_digits = property(attrgetter('digits'))
951 _column_digits = property(lambda self: not callable(self._digits) and self._digits)
952 _column_digits_compute = property(lambda self: callable(self._digits) and self._digits)
954 def convert_to_cache(self, value, record, validate=True):
955 # apply rounding here, otherwise value in cache may be wrong!
957 return float_round(float(value or 0.0), precision_digits=self.digits[1])
959 return float(value or 0.0)
962 class _String(Field):
963 """ Abstract class for string fields. """
966 _column_translate = property(attrgetter('translate'))
967 _related_translate = property(attrgetter('translate'))
968 _description_translate = property(attrgetter('translate'))
972 """ Basic string field, can be length-limited, usually displayed as a
973 single-line string in clients
975 :param int size: the maximum size of values stored for that field
976 :param bool translate: whether the values of this field can be translated
981 _column_size = property(attrgetter('size'))
982 _related_size = property(attrgetter('size'))
983 _description_size = property(attrgetter('size'))
985 def convert_to_cache(self, value, record, validate=True):
986 if value is None or value is False:
988 return ustr(value)[:self.size]
991 """ Text field. Very similar to :class:`~.Char` but used for longer
992 contents and displayed as a multiline text box
994 :param translate: whether the value of this field can be translated
998 def convert_to_cache(self, value, record, validate=True):
999 if value is None or value is False:
1003 class Html(_String):
1005 sanitize = True # whether value must be sanitized
1007 _column_sanitize = property(attrgetter('sanitize'))
1008 _related_sanitize = property(attrgetter('sanitize'))
1009 _description_sanitize = property(attrgetter('sanitize'))
1011 def convert_to_cache(self, value, record, validate=True):
1012 if value is None or value is False:
1014 if validate and self.sanitize:
1015 return html_sanitize(value)
1024 """ Return the current day in the format expected by the ORM.
1025 This function may be used to compute default values.
1027 return date.today().strftime(DATE_FORMAT)
1030 def context_today(record, timestamp=None):
1031 """ Return the current date as seen in the client's timezone in a format
1032 fit for date fields. This method may be used to compute default
1035 :param datetime timestamp: optional datetime value to use instead of
1036 the current date and time (must be a datetime, regular dates
1037 can't be converted between timezones.)
1040 today = timestamp or datetime.now()
1041 context_today = None
1042 tz_name = record._context.get('tz') or record.env.user.tz
1045 today_utc = pytz.timezone('UTC').localize(today, is_dst=False) # UTC = no DST
1046 context_today = today_utc.astimezone(pytz.timezone(tz_name))
1048 _logger.debug("failed to compute context/client-specific today date, using UTC value for `today`",
1050 return (context_today or today).strftime(DATE_FORMAT)
1053 def from_string(value):
1054 """ Convert an ORM `value` into a :class:`date` value. """
1055 value = value[:DATE_LENGTH]
1056 return datetime.strptime(value, DATE_FORMAT).date()
1059 def to_string(value):
1060 """ Convert a :class:`date` value into the format expected by the ORM. """
1061 return value.strftime(DATE_FORMAT)
1063 def convert_to_cache(self, value, record, validate=True):
1066 if isinstance(value, basestring):
1068 # force parsing for validation
1069 self.from_string(value)
1070 return value[:DATE_LENGTH]
1071 return self.to_string(value)
1073 def convert_to_export(self, value, env):
1074 if value and env.context.get('export_raw_data'):
1075 return self.from_string(value)
1076 return bool(value) and ustr(value)
1079 class Datetime(Field):
1084 """ Return the current day and time in the format expected by the ORM.
1085 This function may be used to compute default values.
1087 return datetime.now().strftime(DATETIME_FORMAT)
1090 def context_timestamp(record, timestamp):
1091 """Returns the given timestamp converted to the client's timezone.
1092 This method is *not* meant for use as a _defaults initializer,
1093 because datetime fields are automatically converted upon
1094 display on client side. For _defaults you :meth:`fields.datetime.now`
1095 should be used instead.
1097 :param datetime timestamp: naive datetime value (expressed in UTC)
1098 to be converted to the client timezone
1100 :return: timestamp converted to timezone-aware datetime in context
1103 assert isinstance(timestamp, datetime), 'Datetime instance expected'
1104 tz_name = record._context.get('tz') or record.env.user.tz
1107 utc = pytz.timezone('UTC')
1108 context_tz = pytz.timezone(tz_name)
1109 utc_timestamp = utc.localize(timestamp, is_dst=False) # UTC = no DST
1110 return utc_timestamp.astimezone(context_tz)
1112 _logger.debug("failed to compute context/client-specific timestamp, "
1113 "using the UTC value",
1118 def from_string(value):
1119 """ Convert an ORM `value` into a :class:`datetime` value. """
1120 value = value[:DATETIME_LENGTH]
1121 if len(value) == DATE_LENGTH:
1122 value += " 00:00:00"
1123 return datetime.strptime(value, DATETIME_FORMAT)
1126 def to_string(value):
1127 """ Convert a :class:`datetime` value into the format expected by the ORM. """
1128 return value.strftime(DATETIME_FORMAT)
1130 def convert_to_cache(self, value, record, validate=True):
1133 if isinstance(value, basestring):
1135 # force parsing for validation
1136 self.from_string(value)
1137 value = value[:DATETIME_LENGTH]
1138 if len(value) == DATE_LENGTH:
1139 value += " 00:00:00"
1141 return self.to_string(value)
1143 def convert_to_export(self, value, env):
1144 if value and env.context.get('export_raw_data'):
1145 return self.from_string(value)
1146 return bool(value) and ustr(value)
1149 class Binary(Field):
1153 class Selection(Field):
1155 :param selection: specifies the possible values for this field.
1156 It is given as either a list of pairs (`value`, `string`), or a
1157 model method, or a method name.
1158 :param selection_add: provides an extension of the selection in the case
1159 of an overridden field. It is a list of pairs (`value`, `string`).
1161 The attribute `selection` is mandatory except in the case of
1162 :ref:`related fields <field-related>` or :ref:`field extensions
1163 <field-incremental-definition>`.
1166 selection = None # [(value, string), ...], function or method name
1167 selection_add = None # [(value, string), ...]
1169 def __init__(self, selection=None, string=None, **kwargs):
1170 if callable(selection):
1171 from openerp import api
1172 selection = api.expected(api.model, selection)
1173 super(Selection, self).__init__(selection=selection, string=string, **kwargs)
1175 def _setup_related(self, env):
1176 super(Selection, self)._setup_related(env)
1177 # selection must be computed on related field
1178 field = self.related_field
1179 self.selection = lambda model: field._description_selection(model.env)
1181 def _setup_regular(self, env):
1182 super(Selection, self)._setup_regular(env)
1183 # determine selection (applying extensions)
1184 cls = type(env[self.model_name])
1186 for field in resolve_all_mro(cls, self.name, reverse=True):
1187 if isinstance(field, type(self)):
1188 # We cannot use field.selection or field.selection_add here
1189 # because those attributes are overridden by `set_class_name`.
1190 if 'selection' in field._attrs:
1191 selection = field._attrs['selection']
1192 if 'selection_add' in field._attrs:
1193 selection = selection + field._attrs['selection_add']
1196 self.selection = selection
1198 def _description_selection(self, env):
1199 """ return the selection list (pairs (value, label)); labels are
1200 translated according to context language
1202 selection = self.selection
1203 if isinstance(selection, basestring):
1204 return getattr(env[self.model_name], selection)()
1205 if callable(selection):
1206 return selection(env[self.model_name])
1208 # translate selection labels
1210 name = "%s,%s" % (self.model_name, self.name)
1211 translate = partial(
1212 env['ir.translation']._get_source, name, 'selection', env.lang)
1213 return [(value, translate(label)) for value, label in selection]
1218 def _column_selection(self):
1219 if isinstance(self.selection, basestring):
1220 method = self.selection
1221 return lambda self, *a, **kw: getattr(self, method)(*a, **kw)
1223 return self.selection
1225 def get_values(self, env):
1226 """ return a list of the possible values """
1227 selection = self.selection
1228 if isinstance(selection, basestring):
1229 selection = getattr(env[self.model_name], selection)()
1230 elif callable(selection):
1231 selection = selection(env[self.model_name])
1232 return [value for value, _ in selection]
1234 def convert_to_cache(self, value, record, validate=True):
1236 return value or False
1237 if value in self.get_values(record.env):
1241 raise ValueError("Wrong value for %s: %r" % (self, value))
1243 def convert_to_export(self, value, env):
1244 if not isinstance(self.selection, list):
1245 # FIXME: this reproduces an existing buggy behavior!
1247 for item in self._description_selection(env):
1248 if item[0] == value:
1253 class Reference(Selection):
1257 def __init__(self, selection=None, string=None, **kwargs):
1258 super(Reference, self).__init__(selection=selection, string=string, **kwargs)
1260 _related_size = property(attrgetter('size'))
1262 _column_size = property(attrgetter('size'))
1264 def convert_to_cache(self, value, record, validate=True):
1265 if isinstance(value, BaseModel):
1266 if ((not validate or value._name in self.get_values(record.env))
1267 and len(value) <= 1):
1268 return value.with_env(record.env) or False
1269 elif isinstance(value, basestring):
1270 res_model, res_id = value.split(',')
1271 return record.env[res_model].browse(int(res_id))
1274 raise ValueError("Wrong value for %s: %r" % (self, value))
1276 def convert_to_read(self, value, use_name_get=True):
1277 return "%s,%s" % (value._name, value.id) if value else False
1279 def convert_to_export(self, value, env):
1280 return bool(value) and value.name_get()[0][1]
1282 def convert_to_display_name(self, value):
1283 return ustr(value and value.display_name)
1286 class _Relational(Field):
1287 """ Abstract class for relational fields. """
1289 domain = None # domain for searching values
1290 context = None # context for searching values
1292 _description_relation = property(attrgetter('comodel_name'))
1293 _description_context = property(attrgetter('context'))
1295 def _description_domain(self, env):
1296 return self.domain(env[self.model_name]) if callable(self.domain) else self.domain
1298 _column_obj = property(attrgetter('comodel_name'))
1299 _column_domain = property(attrgetter('domain'))
1300 _column_context = property(attrgetter('context'))
1302 def null(self, env):
1303 return env[self.comodel_name]
1305 def modified(self, records):
1306 # Invalidate cache for self.inverse_fields, too. Note that recomputation
1307 # of fields that depend on self.inverse_fields is already covered by the
1308 # triggers (see above).
1309 spec = super(_Relational, self).modified(records)
1310 for invf in self.inverse_fields:
1311 spec.append((invf, None))
1315 class Many2one(_Relational):
1316 """ The value of such a field is a recordset of size 0 (no
1317 record) or 1 (a single record).
1319 :param comodel_name: name of the target model (string)
1321 :param domain: an optional domain to set on candidate values on the
1322 client side (domain or string)
1324 :param context: an optional context to use on the client side when
1325 handling that field (dictionary)
1327 :param ondelete: what to do when the referred record is deleted;
1328 possible values are: ``'set null'``, ``'restrict'``, ``'cascade'``
1330 :param auto_join: whether JOINs are generated upon search through that
1331 field (boolean, by default ``False``)
1333 :param delegate: set it to ``True`` to make fields of the target model
1334 accessible from the current model (corresponds to ``_inherits``)
1336 The attribute `comodel_name` is mandatory except in the case of related
1337 fields or field extensions.
1340 ondelete = 'set null' # what to do when value is deleted
1341 auto_join = False # whether joins are generated upon search
1342 delegate = False # whether self implements delegation
1344 def __init__(self, comodel_name=None, string=None, **kwargs):
1345 super(Many2one, self).__init__(comodel_name=comodel_name, string=string, **kwargs)
1347 def _setup_regular(self, env):
1348 super(Many2one, self)._setup_regular(env)
1350 # self.inverse_fields is populated by the corresponding One2many field
1352 # determine self.delegate
1353 self.delegate = self.name in env[self.model_name]._inherits.values()
1355 _column_ondelete = property(attrgetter('ondelete'))
1356 _column_auto_join = property(attrgetter('auto_join'))
1358 def _update(self, records, value):
1359 """ Update the cached value of `self` for `records` with `value`. """
1360 records._cache[self] = value
1362 def convert_to_cache(self, value, record, validate=True):
1363 if isinstance(value, (NoneType, int)):
1364 return record.env[self.comodel_name].browse(value)
1365 if isinstance(value, BaseModel):
1366 if value._name == self.comodel_name and len(value) <= 1:
1367 return value.with_env(record.env)
1368 raise ValueError("Wrong value for %s: %r" % (self, value))
1369 elif isinstance(value, tuple):
1370 return record.env[self.comodel_name].browse(value[0])
1371 elif isinstance(value, dict):
1372 return record.env[self.comodel_name].new(value)
1374 return record.env[self.comodel_name].browse(value)
1376 def convert_to_read(self, value, use_name_get=True):
1377 if use_name_get and value:
1378 # evaluate name_get() as superuser, because the visibility of a
1379 # many2one field value (id and name) depends on the current record's
1380 # access rights, and not the value's access rights.
1382 return value.sudo().name_get()[0]
1383 except MissingError:
1384 # Should not happen, unless the foreign key is missing.
1389 def convert_to_write(self, value, target=None, fnames=None):
1392 def convert_to_onchange(self, value):
1395 def convert_to_export(self, value, env):
1396 return bool(value) and value.name_get()[0][1]
1398 def convert_to_display_name(self, value):
1399 return ustr(value.display_name)
1401 def determine_default(self, record):
1402 super(Many2one, self).determine_default(record)
1404 # special case: fields that implement inheritance between models
1405 value = record[self.name]
1407 # the default value cannot be null, use a new record instead
1408 record[self.name] = record.env[self.comodel_name].new()
1411 class UnionUpdate(SpecialValue):
1412 """ Placeholder for a value update; when this value is taken from the cache,
1413 it returns ``record[field.name] | value`` and stores it in the cache.
1415 def __init__(self, field, record, value):
1416 self.args = (field, record, value)
1419 field, record, value = self.args
1420 # in order to read the current field's value, remove self from cache
1421 del record._cache[field]
1422 # read the current field's value, and update it in cache only
1423 record._cache[field] = new_value = record[field.name] | value
1427 class _RelationalMulti(_Relational):
1428 """ Abstract class for relational fields *2many. """
1430 def _update(self, records, value):
1431 """ Update the cached value of `self` for `records` with `value`. """
1432 for record in records:
1433 if self in record._cache:
1434 record._cache[self] = record[self.name] | value
1436 record._cache[self] = UnionUpdate(self, record, value)
1438 def convert_to_cache(self, value, record, validate=True):
1439 if isinstance(value, BaseModel):
1440 if value._name == self.comodel_name:
1441 return value.with_env(record.env)
1442 elif isinstance(value, list):
1443 # value is a list of record ids or commands
1445 record = record.browse() # new record has no value
1446 result = record[self.name]
1447 # modify result with the commands;
1448 # beware to not introduce duplicates in result
1449 for command in value:
1450 if isinstance(command, (tuple, list)):
1452 result += result.new(command[2])
1453 elif command[0] == 1:
1454 result.browse(command[1]).update(command[2])
1455 result += result.browse(command[1]) - result
1456 elif command[0] == 2:
1457 # note: the record will be deleted by write()
1458 result -= result.browse(command[1])
1459 elif command[0] == 3:
1460 result -= result.browse(command[1])
1461 elif command[0] == 4:
1462 result += result.browse(command[1]) - result
1463 elif command[0] == 5:
1464 result = result.browse()
1465 elif command[0] == 6:
1466 result = result.browse(command[2])
1467 elif isinstance(command, dict):
1468 result += result.new(command)
1470 result += result.browse(command) - result
1473 return self.null(record.env)
1474 raise ValueError("Wrong value for %s: %s" % (self, value))
1476 def convert_to_read(self, value, use_name_get=True):
1479 def convert_to_write(self, value, target=None, fnames=None):
1480 # remove/delete former records
1483 result = [(6, 0, set_ids)]
1484 add_existing = lambda id: set_ids.append(id)
1486 tag = 2 if self.type == 'one2many' else 3
1487 result = [(tag, record.id) for record in target[self.name] - value]
1488 add_existing = lambda id: result.append((4, id))
1491 # take all fields in cache, except the inverses of self
1492 fnames = set(value._fields) - set(MAGIC_COLUMNS)
1493 for invf in self.inverse_fields:
1494 fnames.discard(invf.name)
1496 # add new and existing records
1497 for record in value:
1498 if not record.id or record._dirty:
1499 values = dict((k, v) for k, v in record._cache.iteritems() if k in fnames)
1500 values = record._convert_to_write(values)
1502 result.append((0, 0, values))
1504 result.append((1, record.id, values))
1506 add_existing(record.id)
1510 def convert_to_export(self, value, env):
1511 return bool(value) and ','.join(name for id, name in value.name_get())
1513 def convert_to_display_name(self, value):
1514 raise NotImplementedError()
1516 def _compute_related(self, records):
1517 """ Compute the related field `self` on `records`. """
1518 for record in records:
1520 # traverse the intermediate fields, and keep at most one record
1521 for name in self.related[:-1]:
1522 value = value[name][:1]
1523 record[self.name] = value[self.related[-1]]
1526 class One2many(_RelationalMulti):
1527 """ One2many field; the value of such a field is the recordset of all the
1528 records in `comodel_name` such that the field `inverse_name` is equal to
1531 :param comodel_name: name of the target model (string)
1533 :param inverse_name: name of the inverse `Many2one` field in
1534 `comodel_name` (string)
1536 :param domain: an optional domain to set on candidate values on the
1537 client side (domain or string)
1539 :param context: an optional context to use on the client side when
1540 handling that field (dictionary)
1542 :param auto_join: whether JOINs are generated upon search through that
1543 field (boolean, by default ``False``)
1545 :param limit: optional limit to use upon read (integer)
1547 The attributes `comodel_name` and `inverse_name` are mandatory except in
1548 the case of related fields or field extensions.
1551 inverse_name = None # name of the inverse field
1552 auto_join = False # whether joins are generated upon search
1553 limit = None # optional limit to use upon read
1554 copyable = False # o2m are not copied by default
1556 def __init__(self, comodel_name=None, inverse_name=None, string=None, **kwargs):
1557 super(One2many, self).__init__(
1558 comodel_name=comodel_name,
1559 inverse_name=inverse_name,
1564 def _setup_regular(self, env):
1565 super(One2many, self)._setup_regular(env)
1567 if self.inverse_name:
1568 # link self to its inverse field and vice-versa
1569 invf = env[self.comodel_name]._fields[self.inverse_name]
1570 # In some rare cases, a `One2many` field can link to `Int` field
1571 # (res_model/res_id pattern). Only inverse the field if this is
1572 # a `Many2one` field.
1573 if isinstance(invf, Many2one):
1574 self.inverse_fields.append(invf)
1575 invf.inverse_fields.append(self)
1577 _description_relation_field = property(attrgetter('inverse_name'))
1579 _column_fields_id = property(attrgetter('inverse_name'))
1580 _column_auto_join = property(attrgetter('auto_join'))
1581 _column_limit = property(attrgetter('limit'))
1584 class Many2many(_RelationalMulti):
1585 """ Many2many field; the value of such a field is the recordset.
1587 :param comodel_name: name of the target model (string)
1589 The attribute `comodel_name` is mandatory except in the case of related
1590 fields or field extensions.
1592 :param relation: optional name of the table that stores the relation in
1593 the database (string)
1595 :param column1: optional name of the column referring to "these" records
1596 in the table `relation` (string)
1598 :param column2: optional name of the column referring to "those" records
1599 in the table `relation` (string)
1601 The attributes `relation`, `column1` and `column2` are optional. If not
1602 given, names are automatically generated from model names, provided
1603 `model_name` and `comodel_name` are different!
1605 :param domain: an optional domain to set on candidate values on the
1606 client side (domain or string)
1608 :param context: an optional context to use on the client side when
1609 handling that field (dictionary)
1611 :param limit: optional limit to use upon read (integer)
1615 relation = None # name of table
1616 column1 = None # column of table referring to model
1617 column2 = None # column of table referring to comodel
1618 limit = None # optional limit to use upon read
1620 def __init__(self, comodel_name=None, relation=None, column1=None, column2=None,
1621 string=None, **kwargs):
1622 super(Many2many, self).__init__(
1623 comodel_name=comodel_name,
1631 def _setup_regular(self, env):
1632 super(Many2many, self)._setup_regular(env)
1634 if self.store and not self.relation:
1635 model = env[self.model_name]
1636 column = model._columns[self.name]
1637 if not isinstance(column, fields.function):
1638 self.relation, self.column1, self.column2 = column._sql_names(model)
1641 m2m = env.registry._m2m
1642 # if inverse field has already been setup, it is present in m2m
1643 invf = m2m.get((self.relation, self.column2, self.column1))
1645 self.inverse_fields.append(invf)
1646 invf.inverse_fields.append(self)
1648 # add self in m2m, so that its inverse field can find it
1649 m2m[(self.relation, self.column1, self.column2)] = self
1651 _column_rel = property(attrgetter('relation'))
1652 _column_id1 = property(attrgetter('column1'))
1653 _column_id2 = property(attrgetter('column2'))
1654 _column_limit = property(attrgetter('limit'))
1658 """ Special case for field 'id'. """
1660 #: Can't write this!
1663 def __init__(self, string=None, **kwargs):
1664 super(Id, self).__init__(type='integer', string=string, **kwargs)
1666 def to_column(self):
1667 """ to_column() -> fields._column
1671 return fields.integer('ID')
1673 def __get__(self, record, owner):
1675 return self # the field is accessed through the class owner
1678 return record.ensure_one()._ids[0]
1680 def __set__(self, record, value):
1681 raise TypeError("field 'id' cannot be assigned")
1684 # imported here to avoid dependency cycle issues
1685 from openerp import SUPERUSER_ID
1686 from .exceptions import Warning, AccessError, MissingError
1687 from .models import BaseModel, MAGIC_COLUMNS
1688 from .osv import fields