1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2013-2014 OpenERP (<http://www.openerp.com>).
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as
9 # published by the Free Software Foundation, either version 3 of the
10 # License, or (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 ##############################################################################
22 """ High-level objects for fields. """
25 from datetime import date, datetime
26 from functools import partial
27 from operator import attrgetter
32 from types import NoneType
34 from openerp.tools import float_round, ustr, html_sanitize
35 from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as DATE_FORMAT
36 from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT as DATETIME_FORMAT
38 DATE_LENGTH = len(date.today().strftime(DATE_FORMAT))
39 DATETIME_LENGTH = len(datetime.now().strftime(DATETIME_FORMAT))
41 _logger = logging.getLogger(__name__)
44 class SpecialValue(object):
45 """ Encapsulates a value in the cache in place of a normal value. """
46 def __init__(self, value):
51 class FailedValue(SpecialValue):
52 """ Special value that encapsulates an exception instead of a value. """
53 def __init__(self, exception):
54 self.exception = exception
58 def _check_value(value):
59 """ Return `value`, or call its getter if `value` is a :class:`SpecialValue`. """
60 return value.get() if isinstance(value, SpecialValue) else value
63 def resolve_all_mro(cls, name, reverse=False):
64 """ Return the (successively overridden) values of attribute `name` in `cls`
65 in mro order, or inverse mro order if `reverse` is true.
67 klasses = reversed(cls.__mro__) if reverse else cls.__mro__
69 if name in klass.__dict__:
70 yield klass.__dict__[name]
73 def default_compute(field, value):
74 """ Return a compute function for the given default `value`; `value` is
75 either a constant, or a unary function returning the default value.
78 func = value if callable(value) else lambda rec: value
85 class MetaField(type):
86 """ Metaclass for field classes. """
89 def __init__(cls, name, bases, attrs):
90 super(MetaField, cls).__init__(name, bases, attrs)
92 cls.by_type[cls.type] = cls
94 # compute class attributes to avoid calling dir() on fields
96 cls.related_attrs = []
97 cls.description_attrs = []
99 if attr.startswith('_column_'):
100 cls.column_attrs.append((attr[8:], attr))
101 elif attr.startswith('_related_'):
102 cls.related_attrs.append((attr[9:], attr))
103 elif attr.startswith('_description_'):
104 cls.description_attrs.append((attr[13:], attr))
108 """ The field descriptor contains the field definition, and manages accesses
109 and assignments of the corresponding field on records. The following
110 attributes may be provided when instanciating a field:
112 :param string: the label of the field seen by users (string); if not
113 set, the ORM takes the field name in the class (capitalized).
115 :param help: the tooltip of the field seen by users (string)
117 :param readonly: whether the field is readonly (boolean, by default ``False``)
119 :param required: whether the value of the field is required (boolean, by
122 :param index: whether the field is indexed in database (boolean, by
125 :param default: the default value for the field; this is either a static
126 value, or a function taking a recordset and returning a value
128 :param states: a dictionary mapping state values to lists of attribute-value
129 pairs; possible attributes are: 'readonly', 'required', 'invisible'
131 :param groups: comma-separated list of group xml ids (string); this
132 restricts the field access to the users of the given groups only
136 .. rubric:: Computed fields
138 One can define a field whose value is computed instead of simply being
139 read from the database. The attributes that are specific to computed
140 fields are given below. To define such a field, simply provide a value
141 for the attribute `compute`.
143 :param compute: name of a method that computes the field
145 :param inverse: name of a method that inverses the field (optional)
147 :param search: name of a method that implement search on the field (optional)
149 :param store: whether the field is stored in database (boolean, by
150 default ``False`` on computed fields)
152 The methods given for `compute`, `inverse` and `search` are model
153 methods. Their signature is shown in the following example::
155 upper = fields.Char(compute='_compute_upper',
156 inverse='_inverse_upper',
157 search='_search_upper')
160 def _compute_upper(self):
162 self.upper = self.name.upper() if self.name else False
164 def _inverse_upper(self):
166 self.name = self.upper.lower() if self.upper else False
168 def _search_upper(self, operator, value):
169 if operator == 'like':
171 return [('name', operator, value)]
173 The compute method has to assign the field on all records of the invoked
174 recordset. The decorator :meth:`openerp.api.depends` must be applied on
175 the compute method to specify the field dependencies; those dependencies
176 are used to determine when to recompute the field; recomputation is
177 automatic and guarantees cache/database consistency. Note that the same
178 method can be used for several fields, you simply have to assign all the
179 given fields in the method; the method will be invoked once for all
182 By default, a computed field is not stored to the database, and is
183 computed on-the-fly. Adding the attribute ``store=True`` will store the
184 field's values in the database. The advantage of a stored field is that
185 searching on that field is done by the database itself. The disadvantage
186 is that it requires database updates when the field must be recomputed.
188 The inverse method, as its name says, does the inverse of the compute
189 method: the invoked records have a value for the field, and you must
190 apply the necessary changes on the field dependencies such that the
191 computation gives the expected value. Note that a computed field without
192 an inverse method is readonly by default.
194 The search method is invoked when processing domains before doing an
195 actual search on the model. It must return a domain equivalent to the
196 condition: `field operator value`.
200 .. rubric:: Related fields
202 The value of a related field is given by following a sequence of
203 relational fields and reading a field on the reached model. The complete
204 sequence of fields to traverse is specified by the attribute
206 :param related: sequence of field names
208 The value of some attributes from related fields are automatically taken
209 from the source field, when it makes sense. Examples are the attributes
210 `string` or `selection` on selection fields.
212 By default, the values of related fields are not stored to the database.
213 Add the attribute ``store=True`` to make it stored, just like computed
214 fields. Related fields are automatically recomputed when their
215 dependencies are modified.
217 .. _field-company-dependent:
219 .. rubric:: Company-dependent fields
221 Formerly known as 'property' fields, the value of those fields depends
222 on the company. In other words, users that belong to different companies
223 may see different values for the field on a given record.
225 :param company_dependent: whether the field is company-dependent (boolean)
227 .. _field-incremental-definition:
229 .. rubric:: Incremental definition
231 A field is defined as class attribute on a model class. If the model is
232 extended (see :class:`BaseModel`), one can also extend the field
233 definition by redefining a field with the same name and same type on the
234 subclass. In that case, the attributes of the field are taken from the
235 parent class and overridden by the ones given in subclasses.
237 For instance, the second class below only adds a tooltip on the field
240 class First(models.Model):
242 state = fields.Selection([...], required=True)
244 class Second(models.Model):
246 state = fields.Selection(help="Blah blah blah")
249 __metaclass__ = MetaField
251 _attrs = None # dictionary with all field attributes
252 _free_attrs = None # list of semantic-free attribute names
254 automatic = False # whether the field is automatically created ("magic" field)
255 _origin = None # the column or field interfaced by self, if any
257 name = None # name of the field
258 type = None # type of the field (string)
259 relational = False # whether the field is a relational one
260 model_name = None # name of the model of this field
261 comodel_name = None # name of the model of values (if relational)
262 inverse_field = None # inverse field (object), if it exists
264 store = True # whether the field is stored in database
265 index = False # whether the field is indexed in database
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 company_dependent = False # whether `self` is company-dependent (property field)
274 default = None # default value
276 string = None # field label
277 help = None # field tooltip
281 groups = False # csv list of group xml ids
283 def __init__(self, string=None, **kwargs):
284 kwargs['string'] = string
285 self._attrs = {key: val for key, val in kwargs.iteritems() if val is not None}
286 self._free_attrs = []
288 def copy(self, **kwargs):
289 """ make a copy of `self`, possibly modified with parameters `kwargs` """
291 field._attrs = {key: val for key, val in kwargs.iteritems() if val is not None}
292 field._free_attrs = list(self._free_attrs)
295 def set_class_name(self, cls, name):
296 """ Assign the model class and field name of `self`. """
297 self.model_name = cls._name
300 # determine all inherited field attributes
302 for field in resolve_all_mro(cls, name, reverse=True):
303 if isinstance(field, type(self)):
304 attrs.update(field._attrs)
307 attrs.update(self._attrs) # necessary in case self is not in cls
309 # initialize `self` with `attrs`
310 if attrs.get('compute'):
311 # by default, computed fields are not stored, not copied and readonly
312 attrs['store'] = attrs.get('store', False)
313 attrs['copy'] = attrs.get('copy', False)
314 attrs['readonly'] = attrs.get('readonly', not attrs.get('inverse'))
315 if attrs.get('related'):
316 # by default, related fields are not stored
317 attrs['store'] = attrs.get('store', False)
319 # attribute is copyable because there is also a copy() method
320 attrs['copyable'] = attrs.pop('copy')
322 for attr, value in attrs.iteritems():
323 if not hasattr(self, attr):
324 self._free_attrs.append(attr)
325 setattr(self, attr, value)
328 self.string = name.replace('_', ' ').capitalize()
333 return "%s.%s" % (self.model_name, self.name)
336 return "%s.%s" % (self.model_name, self.name)
338 ############################################################################
344 """ Prepare `self` for a new setup. """
345 self._setup_done = False
346 # self._triggers is a set of pairs (field, path) that represents the
347 # computed fields that depend on `self`. When `self` is modified, it
348 # invalidates the cache of each `field`, and registers the records to
349 # recompute based on `path`. See method `modified` below for details.
350 self._triggers = set()
351 self.inverse_field = None
353 def setup(self, env):
354 """ Complete the setup of `self` (dependencies, recomputation triggers,
355 and other properties). This method is idempotent: it has no effect
356 if `self` has already been set up.
358 if not self._setup_done:
359 self._setup_done = True
362 def _setup(self, env):
363 """ Do the actual setup of `self`. """
365 self._setup_related(env)
367 self._setup_regular(env)
369 # put invalidation/recomputation triggers on field dependencies
370 model = env[self.model_name]
371 for path in self.depends:
372 self._setup_dependency([], model, path.split('.'))
374 # put invalidation triggers on model dependencies
375 for dep_model_name, field_names in model._depends.iteritems():
376 dep_model = env[dep_model_name]
377 for field_name in field_names:
378 field = dep_model._fields[field_name]
379 field._triggers.add((self, None))
382 # Setup of related fields
385 def _setup_related(self, env):
386 """ Setup the attributes of a related field. """
387 # fix the type of self.related if necessary
388 if isinstance(self.related, basestring):
389 self.related = tuple(self.related.split('.'))
391 # determine the related field, and make sure it is set up
392 recs = env[self.model_name]
393 for name in self.related[:-1]:
395 field = self.related_field = recs._fields[self.related[-1]]
398 # check type consistency
399 if self.type != field.type:
400 raise Warning("Type of related field %s is inconsistent with %s" % (self, field))
402 # determine dependencies, compute, inverse, and search
403 self.depends = ('.'.join(self.related),)
404 self.compute = self._compute_related
405 self.inverse = self._inverse_related
406 self.search = self._search_related
408 # copy attributes from field to self (string, help, etc.)
409 for attr, prop in self.related_attrs:
410 if not getattr(self, attr):
411 setattr(self, attr, getattr(field, prop))
413 def _compute_related(self, records):
414 """ Compute the related field `self` on `records`. """
415 for record in records:
416 # bypass access rights check when traversing the related path
417 value = record.sudo() if record.id else record
418 # traverse the intermediate fields, and keep at most one record
419 for name in self.related[:-1]:
420 value = value[name][:1]
421 record[self.name] = value[self.related[-1]]
423 def _inverse_related(self, records):
424 """ Inverse the related field `self` on `records`. """
425 for record in records:
427 # traverse the intermediate fields, and keep at most one record
428 for name in self.related[:-1]:
429 other = other[name][:1]
431 other[self.related[-1]] = record[self.name]
433 def _search_related(self, records, operator, value):
434 """ Determine the domain to search on field `self`. """
435 return [('.'.join(self.related), operator, value)]
437 # properties used by _setup_related() to copy values from related field
438 _related_string = property(attrgetter('string'))
439 _related_help = property(attrgetter('help'))
440 _related_readonly = property(attrgetter('readonly'))
441 _related_groups = property(attrgetter('groups'))
444 # Setup of non-related fields
447 def _setup_regular(self, env):
448 """ Setup the attributes of a non-related field. """
449 recs = env[self.model_name]
451 def make_depends(deps):
452 return tuple(deps(recs) if callable(deps) else deps)
454 # transform self.default into self.compute
455 if self.default is not None and self.compute is None:
456 self.compute = default_compute(self, self.default)
458 # convert compute into a callable and determine depends
459 if isinstance(self.compute, basestring):
460 # if the compute method has been overridden, concatenate all their _depends
462 for method in resolve_all_mro(type(recs), self.compute, reverse=True):
463 self.depends += make_depends(getattr(method, '_depends', ()))
464 self.compute = getattr(type(recs), self.compute)
466 self.depends = make_depends(getattr(self.compute, '_depends', ()))
468 # convert inverse and search into callables
469 if isinstance(self.inverse, basestring):
470 self.inverse = getattr(type(recs), self.inverse)
471 if isinstance(self.search, basestring):
472 self.search = getattr(type(recs), self.search)
474 def _setup_dependency(self, path0, model, path1):
475 """ Make `self` depend on `model`; `path0 + path1` is a dependency of
476 `self`, and `path0` is the sequence of field names from `self.model`
480 head, tail = path1[0], path1[1:]
483 # special case: add triggers on all fields of model (except self)
484 fields = set(model._fields.itervalues()) - set([self])
486 fields = [model._fields[head]]
490 _logger.debug("Field %s is recursively defined", self)
491 self.recursive = True
496 #_logger.debug("Add trigger on %s to recompute %s", field, self)
497 field._triggers.add((self, '.'.join(path0 or ['id'])))
499 # add trigger on inverse field, too
500 if field.inverse_field:
501 #_logger.debug("Add trigger on %s to recompute %s", field.inverse_field, self)
502 field.inverse_field._triggers.add((self, '.'.join(path0 + [head])))
504 # recursively traverse the dependency
506 comodel = env[field.comodel_name]
507 self._setup_dependency(path0 + [head], comodel, tail)
510 def dependents(self):
511 """ Return the computed fields that depend on `self`. """
512 return (field for field, path in self._triggers)
514 ############################################################################
519 def get_description(self, env):
520 """ Return a dictionary that describes the field `self`. """
521 desc = {'type': self.type}
524 # if the corresponding column is a function field, check the column
525 column = env[self.model_name]._columns.get(self.name)
526 desc['store'] = bool(getattr(column, 'store', True))
528 desc['store'] = False
529 # determine other attributes
530 for attr, prop in self.description_attrs:
531 value = getattr(self, prop)
538 # properties used by get_description()
539 _description_depends = property(attrgetter('depends'))
540 _description_related = property(attrgetter('related'))
541 _description_company_dependent = property(attrgetter('company_dependent'))
542 _description_readonly = property(attrgetter('readonly'))
543 _description_required = property(attrgetter('required'))
544 _description_states = property(attrgetter('states'))
545 _description_groups = property(attrgetter('groups'))
547 def _description_string(self, env):
548 if self.string and env.lang:
549 name = "%s,%s" % (self.model_name, self.name)
550 trans = env['ir.translation']._get_source(name, 'field', env.lang)
551 return trans or self.string
554 def _description_help(self, env):
555 if self.help and env.lang:
556 name = "%s,%s" % (self.model_name, self.name)
557 trans = env['ir.translation']._get_source(name, 'help', env.lang)
558 return trans or self.help
561 ############################################################################
563 # Conversion to column instance
567 """ return a low-level field object corresponding to `self` """
570 assert isinstance(self._origin, fields._column)
573 _logger.debug("Create fields._column for Field %s", self)
575 for attr, prop in self.column_attrs:
576 args[attr] = getattr(self, prop)
577 for attr in self._free_attrs:
578 args[attr] = getattr(self, attr)
580 if self.company_dependent:
581 # company-dependent fields are mapped to former property fields
582 args['type'] = self.type
583 args['relation'] = self.comodel_name
584 return fields.property(**args)
586 return getattr(fields, self.type)(**args)
588 # properties used by to_column() to create a column instance
589 _column_copy = property(attrgetter('copyable'))
590 _column_select = property(attrgetter('index'))
591 _column_string = property(attrgetter('string'))
592 _column_help = property(attrgetter('help'))
593 _column_readonly = property(attrgetter('readonly'))
594 _column_required = property(attrgetter('required'))
595 _column_states = property(attrgetter('states'))
596 _column_groups = property(attrgetter('groups'))
598 ############################################################################
600 # Conversion of values
604 """ return the null value for this field in the given environment """
607 def convert_to_cache(self, value, env, validate=True):
608 """ convert `value` to the cache level in `env`; `value` may come from
609 an assignment, or have the format of methods :meth:`BaseModel.read`
610 or :meth:`BaseModel.write`
612 :param bool validate: when True, field-specific validation of
613 `value` will be performed
617 def convert_to_read(self, value, use_name_get=True):
618 """ convert `value` from the cache to a value as returned by method
619 :meth:`BaseModel.read`
621 :param bool use_name_get: when True, value's diplay name will
622 be computed using :meth:`BaseModel.name_get`, if relevant
627 def convert_to_write(self, value, target=None, fnames=None):
628 """ convert `value` from the cache to a valid value for method
629 :meth:`BaseModel.write`.
631 :param target: optional, the record to be modified with this value
632 :param fnames: for relational fields only, an optional collection of
633 field names to convert
635 return self.convert_to_read(value)
637 def convert_to_onchange(self, value):
638 """ convert `value` from the cache to a valid value for an onchange
641 return self.convert_to_write(value)
643 def convert_to_export(self, value, env):
644 """ convert `value` from the cache to a valid value for export. The
645 parameter `env` is given for managing translations.
647 if env.context.get('export_raw_data'):
649 return bool(value) and ustr(value)
651 def convert_to_display_name(self, value):
652 """ convert `value` from the cache to a suitable display name. """
655 ############################################################################
660 def __get__(self, record, owner):
661 """ return the value of field `self` on `record` """
663 return self # the field is accessed through the owner class
666 # null record -> return the null value for this field
667 return self.null(record.env)
669 # only a single record may be accessed
673 return record._cache[self]
677 # cache miss, retrieve value
679 # normal record -> read or compute value for this field
680 self.determine_value(record)
682 # new record -> compute default value for this field
683 record.add_default_value(self)
685 # the result should be in cache now
686 return record._cache[self]
688 def __set__(self, record, value):
689 """ set the value of field `self` on `record` """
692 # only a single record may be updated
695 # adapt value to the cache level
696 value = self.convert_to_cache(value, env)
698 if env.in_draft or not record.id:
699 # determine dependent fields
700 spec = self.modified_draft(record)
702 # set value in cache, inverse field, and mark record as dirty
703 record._cache[self] = value
705 if self.inverse_field:
706 self.inverse_field._update(value, record)
709 # determine more dependent fields, and invalidate them
711 spec += self.modified_draft(record)
715 # simply write to the database, and update cache
716 record.write({self.name: self.convert_to_write(value)})
717 record._cache[self] = value
719 ############################################################################
721 # Computation of field values
724 def _compute_value(self, records):
725 """ Invoke the compute method on `records`. """
726 # mark the computed fields failed in cache, so that access before
727 # computation raises an exception
728 exc = Warning("Field %s is accessed before being computed." % self)
729 for field in self.computed_fields:
730 records._cache[field] = FailedValue(exc)
731 records.env.computed[field].update(records._ids)
732 self.compute(records)
733 for field in self.computed_fields:
734 records.env.computed[field].difference_update(records._ids)
736 def compute_value(self, records):
737 """ Invoke the compute method on `records`; the results are in cache. """
738 with records.env.do_in_draft():
740 self._compute_value(records)
742 # some record is missing, retry on existing records only
743 self._compute_value(records.exists())
745 def determine_value(self, record):
746 """ Determine the value of `self` for `record`. """
749 if self.store and not (self.depends and env.in_draft):
750 # this is a stored field
752 # this is a stored computed field, check for recomputation
753 recs = record._recompute_check(self)
755 # recompute the value (only in cache)
756 self.compute_value(recs)
757 # HACK: if result is in the wrong cache, copy values
759 for source, target in zip(recs, recs.with_env(env)):
761 values = target._convert_to_cache({
762 f.name: source[f.name] for f in self.computed_fields
764 except MissingError as e:
765 values = FailedValue(e)
766 target._cache.update(values)
767 # the result is saved to database by BaseModel.recompute()
770 # read the field from database
771 record._prefetch_field(self)
774 # this is either a non-stored computed field, or a stored computed
775 # field in draft mode
777 self.compute_value(record)
779 recs = record._in_cache_without(self)
780 self.compute_value(recs)
783 # this is a non-stored non-computed field
784 record._cache[self] = self.null(env)
786 def determine_default(self, record):
787 """ determine the default value of field `self` on `record` """
789 self._compute_value(record)
791 record._cache[self] = SpecialValue(self.null(record.env))
793 def determine_inverse(self, records):
794 """ Given the value of `self` on `records`, inverse the computation. """
796 self.inverse(records)
798 def determine_domain(self, records, operator, value):
799 """ Return a domain representing a condition on `self`. """
801 return self.search(records, operator, value)
803 return [(self.name, operator, value)]
805 ############################################################################
807 # Notification when fields are modified
810 def modified(self, records):
811 """ Notify that field `self` has been modified on `records`: prepare the
812 fields/records to recompute, and return a spec indicating what to
815 # invalidate the fields that depend on self, and prepare recomputation
816 spec = [(self, records._ids)]
817 for field, path in self._triggers:
818 if path and field.store:
819 # don't move this line to function top, see log
820 env = records.env(user=SUPERUSER_ID, context={'active_test': False})
821 target = env[field.model_name].search([(path, 'in', records.ids)])
823 spec.append((field, target._ids))
824 target.with_env(records.env)._recompute_todo(field)
826 spec.append((field, None))
830 def modified_draft(self, records):
831 """ Same as :meth:`modified`, but in draft mode. """
834 # invalidate the fields on the records in cache that depend on
835 # `records`, except fields currently being computed
837 for field, path in self._triggers:
838 target = env[field.model_name]
839 computed = target.browse(env.computed[field])
841 target = records - computed
843 target = (target.browse(env.cache[field]) - computed).filtered(
844 lambda rec: rec._mapped_cache(path) & records
847 target = target.browse(env.cache[field]) - computed
850 spec.append((field, target._ids))
855 class Boolean(Field):
856 """ Boolean field. """
859 def convert_to_cache(self, value, env, validate=True):
862 def convert_to_export(self, value, env):
863 if env.context.get('export_raw_data'):
868 class Integer(Field):
869 """ Integer field. """
872 def convert_to_cache(self, value, env, validate=True):
873 return int(value or 0)
875 def convert_to_read(self, value, use_name_get=True):
876 # Integer values greater than 2^31-1 are not supported in pure XMLRPC,
877 # so we have to pass them as floats :-(
878 if value and value > xmlrpclib.MAXINT:
882 def _update(self, records, value):
883 # special case, when an integer field is used as inverse for a one2many
884 records._cache[self] = value.id or 0
888 """ Float field. The precision digits are given by the attribute
890 :param digits: a pair (total, decimal), or a function taking a database
891 cursor and returning a pair (total, decimal)
895 _digits = None # digits argument passed to class initializer
896 digits = None # digits as computed by setup()
898 def __init__(self, string=None, digits=None, **kwargs):
899 super(Float, self).__init__(string=string, _digits=digits, **kwargs)
901 def _setup_regular(self, env):
902 super(Float, self)._setup_regular(env)
903 self.digits = self._digits(env.cr) if callable(self._digits) else self._digits
905 _related_digits = property(attrgetter('digits'))
907 _description_digits = property(attrgetter('digits'))
909 _column_digits = property(lambda self: not callable(self._digits) and self._digits)
910 _column_digits_compute = property(lambda self: callable(self._digits) and self._digits)
912 def convert_to_cache(self, value, env, validate=True):
913 # apply rounding here, otherwise value in cache may be wrong!
915 return float_round(float(value or 0.0), precision_digits=self.digits[1])
917 return float(value or 0.0)
920 class _String(Field):
921 """ Abstract class for string fields. """
924 _column_translate = property(attrgetter('translate'))
925 _related_translate = property(attrgetter('translate'))
926 _description_translate = property(attrgetter('translate'))
932 :param size: the maximum size of values stored for that field (integer,
935 :param translate: whether the value of the field has translations
936 (boolean, by default ``False``)
942 _column_size = property(attrgetter('size'))
943 _related_size = property(attrgetter('size'))
944 _description_size = property(attrgetter('size'))
946 def convert_to_cache(self, value, env, validate=True):
947 return bool(value) and ustr(value)[:self.size]
951 """ Text field. Very similar to :class:`Char`, but typically for longer
954 :param translate: whether the value of the field has translations
955 (boolean, by default ``False``)
960 def convert_to_cache(self, value, env, validate=True):
961 return bool(value) and ustr(value)
968 def convert_to_cache(self, value, env, validate=True):
969 return bool(value) and html_sanitize(value)
978 """ Return the current day in the format expected by the ORM.
979 This function may be used to compute default values.
981 return date.today().strftime(DATE_FORMAT)
984 def context_today(record, timestamp=None):
985 """ Return the current date as seen in the client's timezone in a format
986 fit for date fields. This method may be used to compute default
989 :param datetime timestamp: optional datetime value to use instead of
990 the current date and time (must be a datetime, regular dates
991 can't be converted between timezones.)
994 today = timestamp or datetime.now()
996 tz_name = record._context.get('tz') or record.env.user.tz
999 today_utc = pytz.timezone('UTC').localize(today, is_dst=False) # UTC = no DST
1000 context_today = today_utc.astimezone(pytz.timezone(tz_name))
1002 _logger.debug("failed to compute context/client-specific today date, using UTC value for `today`",
1004 return (context_today or today).strftime(DATE_FORMAT)
1007 def from_string(value):
1008 """ Convert an ORM `value` into a :class:`date` value. """
1009 value = value[:DATE_LENGTH]
1010 return datetime.strptime(value, DATE_FORMAT).date()
1013 def to_string(value):
1014 """ Convert a :class:`date` value into the format expected by the ORM. """
1015 return value.strftime(DATE_FORMAT)
1017 def convert_to_cache(self, value, env, validate=True):
1020 if isinstance(value, basestring):
1021 value = self.from_string(value)
1022 return value.strftime(DATE_FORMAT)
1024 def convert_to_export(self, value, env):
1025 if value and env.context.get('export_raw_data'):
1026 return self.from_string(value)
1027 return bool(value) and ustr(value)
1030 class Datetime(Field):
1031 """ Datetime field. """
1036 """ Return the current day and time in the format expected by the ORM.
1037 This function may be used to compute default values.
1039 return datetime.now().strftime(DATETIME_FORMAT)
1042 def context_timestamp(record, timestamp):
1043 """Returns the given timestamp converted to the client's timezone.
1044 This method is *not* meant for use as a _defaults initializer,
1045 because datetime fields are automatically converted upon
1046 display on client side. For _defaults you :meth:`fields.datetime.now`
1047 should be used instead.
1049 :param datetime timestamp: naive datetime value (expressed in UTC)
1050 to be converted to the client timezone
1052 :return: timestamp converted to timezone-aware datetime in context
1055 assert isinstance(timestamp, datetime), 'Datetime instance expected'
1056 tz_name = record._context.get('tz') or record.env.user.tz
1059 utc = pytz.timezone('UTC')
1060 context_tz = pytz.timezone(tz_name)
1061 utc_timestamp = utc.localize(timestamp, is_dst=False) # UTC = no DST
1062 return utc_timestamp.astimezone(context_tz)
1064 _logger.debug("failed to compute context/client-specific timestamp, "
1065 "using the UTC value",
1070 def from_string(value):
1071 """ Convert an ORM `value` into a :class:`datetime` value. """
1072 value = value[:DATETIME_LENGTH]
1073 if len(value) == DATE_LENGTH:
1074 value += " 00:00:00"
1075 return datetime.strptime(value, DATETIME_FORMAT)
1078 def to_string(value):
1079 """ Convert a :class:`datetime` value into the format expected by the ORM. """
1080 return value.strftime(DATETIME_FORMAT)
1082 def convert_to_cache(self, value, env, validate=True):
1085 if isinstance(value, basestring):
1086 value = self.from_string(value)
1087 return value.strftime(DATETIME_FORMAT)
1089 def convert_to_export(self, value, env):
1090 if value and env.context.get('export_raw_data'):
1091 return self.from_string(value)
1092 return bool(value) and ustr(value)
1095 class Binary(Field):
1096 """ Binary field. """
1100 class Selection(Field):
1101 """ Selection field.
1103 :param selection: specifies the possible values for this field.
1104 It is given as either a list of pairs (`value`, `string`), or a
1105 model method, or a method name.
1107 The attribute `selection` is mandatory except in the case of related
1108 fields (see :ref:`field-related`) or field extensions
1109 (see :ref:`field-incremental-definition`).
1112 selection = None # [(value, string), ...], model method or method name
1114 def __init__(self, selection=None, string=None, **kwargs):
1115 if callable(selection):
1116 from openerp import api
1117 selection = api.expected(api.model, selection)
1118 super(Selection, self).__init__(selection=selection, string=string, **kwargs)
1120 def _setup_related(self, env):
1121 super(Selection, self)._setup_related(env)
1122 # selection must be computed on related field
1123 field = self.related_field
1124 self.selection = lambda model: field._description_selection(model.env)
1126 def _description_selection(self, env):
1127 """ return the selection list (pairs (value, label)); labels are
1128 translated according to context language
1130 selection = self.selection
1131 if isinstance(selection, basestring):
1132 return getattr(env[self.model_name], selection)()
1133 if callable(selection):
1134 return selection(env[self.model_name])
1136 # translate selection labels
1138 name = "%s,%s" % (self.model_name, self.name)
1139 translate = partial(
1140 env['ir.translation']._get_source, name, 'selection', env.lang)
1141 return [(value, translate(label)) for value, label in selection]
1146 def _column_selection(self):
1147 if isinstance(self.selection, basestring):
1148 method = self.selection
1149 return lambda self, *a, **kw: getattr(self, method)(*a, **kw)
1151 return self.selection
1153 def get_values(self, env):
1154 """ return a list of the possible values """
1155 selection = self.selection
1156 if isinstance(selection, basestring):
1157 selection = getattr(env[self.model_name], selection)()
1158 elif callable(selection):
1159 selection = selection(env[self.model_name])
1160 return [value for value, _ in selection]
1162 def convert_to_cache(self, value, env, validate=True):
1164 return value or False
1165 if value in self.get_values(env):
1169 raise ValueError("Wrong value for %s: %r" % (self, value))
1171 def convert_to_export(self, value, env):
1172 if not isinstance(self.selection, list):
1173 # FIXME: this reproduces an existing buggy behavior!
1175 for item in self._description_selection(env):
1176 if item[0] == value:
1181 class Reference(Selection):
1182 """ Reference field.
1184 :param selection: specifies the possible model names for this field.
1185 It is given as either a list of pairs (`value`, `string`), or a
1186 model method, or a method name.
1188 The attribute `selection` is mandatory except in the case of related
1189 fields (see :ref:`field-related`) or field extensions
1190 (see :ref:`field-incremental-definition`).
1195 def __init__(self, selection=None, string=None, **kwargs):
1196 super(Reference, self).__init__(selection=selection, string=string, **kwargs)
1198 _related_size = property(attrgetter('size'))
1200 _column_size = property(attrgetter('size'))
1202 def convert_to_cache(self, value, env, validate=True):
1203 if isinstance(value, BaseModel):
1204 if ((not validate or value._name in self.get_values(env))
1205 and len(value) <= 1):
1206 return value.with_env(env) or False
1207 elif isinstance(value, basestring):
1208 res_model, res_id = value.split(',')
1209 return env[res_model].browse(int(res_id))
1212 raise ValueError("Wrong value for %s: %r" % (self, value))
1214 def convert_to_read(self, value, use_name_get=True):
1215 return "%s,%s" % (value._name, value.id) if value else False
1217 def convert_to_export(self, value, env):
1218 return bool(value) and value.name_get()[0][1]
1220 def convert_to_display_name(self, value):
1221 return ustr(value and value.display_name)
1224 class _Relational(Field):
1225 """ Abstract class for relational fields. """
1227 domain = None # domain for searching values
1228 context = None # context for searching values
1230 _description_relation = property(attrgetter('comodel_name'))
1231 _description_context = property(attrgetter('context'))
1233 def _description_domain(self, env):
1234 return self.domain(env[self.model_name]) if callable(self.domain) else self.domain
1236 _column_obj = property(attrgetter('comodel_name'))
1237 _column_domain = property(attrgetter('domain'))
1238 _column_context = property(attrgetter('context'))
1240 def null(self, env):
1241 return env[self.comodel_name]
1243 def modified(self, records):
1244 # Invalidate cache for self.inverse_field, too. Note that recomputation
1245 # of fields that depend on self.inverse_field is already covered by the
1246 # triggers (see above).
1247 spec = super(_Relational, self).modified(records)
1248 if self.inverse_field:
1249 spec.append((self.inverse_field, None))
1253 class Many2one(_Relational):
1254 """ Many2one field; the value of such a field is a recordset of size 0 (no
1255 record) or 1 (a single record).
1257 :param comodel_name: name of the target model (string)
1259 :param domain: an optional domain to set on candidate values on the
1260 client side (domain or string)
1262 :param context: an optional context to use on the client side when
1263 handling that field (dictionary)
1265 :param ondelete: what to do when the referred record is deleted;
1266 possible values are: ``'set null'``, ``'restrict'``, ``'cascade'``
1268 :param auto_join: whether JOINs are generated upon search through that
1269 field (boolean, by default ``False``)
1271 :param delegate: set it to ``True`` to make fields of the target model
1272 accessible from the current model (corresponds to ``_inherits``)
1274 The attribute `comodel_name` is mandatory except in the case of related
1275 fields or field extensions.
1278 ondelete = 'set null' # what to do when value is deleted
1279 auto_join = False # whether joins are generated upon search
1280 delegate = False # whether self implements delegation
1282 def __init__(self, comodel_name=None, string=None, **kwargs):
1283 super(Many2one, self).__init__(comodel_name=comodel_name, string=string, **kwargs)
1285 def _setup_regular(self, env):
1286 super(Many2one, self)._setup_regular(env)
1288 # self.inverse_field is determined by the corresponding One2many field
1290 # determine self.delegate
1291 self.delegate = self.name in env[self.model_name]._inherits.values()
1293 _column_ondelete = property(attrgetter('ondelete'))
1294 _column_auto_join = property(attrgetter('auto_join'))
1296 def _update(self, records, value):
1297 """ Update the cached value of `self` for `records` with `value`. """
1298 records._cache[self] = value
1300 def convert_to_cache(self, value, env, validate=True):
1301 if isinstance(value, (NoneType, int)):
1302 return env[self.comodel_name].browse(value)
1303 if isinstance(value, BaseModel):
1304 if value._name == self.comodel_name and len(value) <= 1:
1305 return value.with_env(env)
1306 raise ValueError("Wrong value for %s: %r" % (self, value))
1307 elif isinstance(value, tuple):
1308 return env[self.comodel_name].browse(value[0])
1309 elif isinstance(value, dict):
1310 return env[self.comodel_name].new(value)
1312 return env[self.comodel_name].browse(value)
1314 def convert_to_read(self, value, use_name_get=True):
1315 if use_name_get and value:
1316 # evaluate name_get() as superuser, because the visibility of a
1317 # many2one field value (id and name) depends on the current record's
1318 # access rights, and not the value's access rights.
1319 return value.sudo().name_get()[0]
1323 def convert_to_write(self, value, target=None, fnames=None):
1324 return bool(value) and (value.id or value._convert_to_write(value._cache))
1326 def convert_to_onchange(self, value):
1329 def convert_to_export(self, value, env):
1330 return bool(value) and value.name_get()[0][1]
1332 def convert_to_display_name(self, value):
1333 return ustr(value.display_name)
1335 def determine_default(self, record):
1336 super(Many2one, self).determine_default(record)
1338 # special case: fields that implement inheritance between models
1339 value = record[self.name]
1341 # the default value cannot be null, use a new record instead
1342 record[self.name] = record.env[self.comodel_name].new()
1345 class _RelationalMulti(_Relational):
1346 """ Abstract class for relational fields *2many. """
1348 def _update(self, records, value):
1349 """ Update the cached value of `self` for `records` with `value`. """
1350 for record in records:
1351 record._cache[self] = record[self.name] | value
1353 def convert_to_cache(self, value, env, validate=True):
1354 if isinstance(value, BaseModel):
1355 if value._name == self.comodel_name:
1356 return value.with_env(env)
1357 elif isinstance(value, list):
1358 # value is a list of record ids or commands
1359 result = env[self.comodel_name]
1360 for command in value:
1361 if isinstance(command, (tuple, list)):
1363 result += result.new(command[2])
1364 elif command[0] == 1:
1365 record = result.browse(command[1])
1366 record.update(command[2])
1368 elif command[0] == 2:
1370 elif command[0] == 3:
1372 elif command[0] == 4:
1373 result += result.browse(command[1])
1374 elif command[0] == 5:
1375 result = result.browse()
1376 elif command[0] == 6:
1377 result = result.browse(command[2])
1378 elif isinstance(command, dict):
1379 result += result.new(command)
1381 result += result.browse(command)
1384 return self.null(env)
1385 raise ValueError("Wrong value for %s: %s" % (self, value))
1387 def convert_to_read(self, value, use_name_get=True):
1390 def convert_to_write(self, value, target=None, fnames=None):
1391 # remove/delete former records
1394 result = [(6, 0, set_ids)]
1395 add_existing = lambda id: set_ids.append(id)
1397 tag = 2 if self.type == 'one2many' else 3
1398 result = [(tag, record.id) for record in target[self.name] - value]
1399 add_existing = lambda id: result.append((4, id))
1402 # take all fields in cache, except the inverse of self
1403 fnames = set(value._fields) - set(MAGIC_COLUMNS)
1404 if self.inverse_field:
1405 fnames.discard(self.inverse_field.name)
1407 # add new and existing records
1408 for record in value:
1409 if not record.id or record._dirty:
1410 values = dict((k, v) for k, v in record._cache.iteritems() if k in fnames)
1411 values = record._convert_to_write(values)
1413 result.append((0, 0, values))
1415 result.append((1, record.id, values))
1417 add_existing(record.id)
1421 def convert_to_export(self, value, env):
1422 return bool(value) and ','.join(name for id, name in value.name_get())
1424 def convert_to_display_name(self, value):
1425 raise NotImplementedError()
1428 class One2many(_RelationalMulti):
1429 """ One2many field; the value of such a field is the recordset of all the
1430 records in `comodel_name` such that the field `inverse_name` is equal to
1433 :param comodel_name: name of the target model (string)
1435 :param inverse_name: name of the inverse `Many2one` field in
1436 `comodel_name` (string)
1438 :param domain: an optional domain to set on candidate values on the
1439 client side (domain or string)
1441 :param context: an optional context to use on the client side when
1442 handling that field (dictionary)
1444 :param auto_join: whether JOINs are generated upon search through that
1445 field (boolean, by default ``False``)
1447 :param limit: optional limit to use upon read (integer)
1449 The attributes `comodel_name` and `inverse_name` are mandatory except in
1450 the case of related fields or field extensions.
1453 inverse_name = None # name of the inverse field
1454 auto_join = False # whether joins are generated upon search
1455 limit = None # optional limit to use upon read
1456 copyable = False # o2m are not copied by default
1458 def __init__(self, comodel_name=None, inverse_name=None, string=None, **kwargs):
1459 super(One2many, self).__init__(
1460 comodel_name=comodel_name,
1461 inverse_name=inverse_name,
1466 def _setup_regular(self, env):
1467 super(One2many, self)._setup_regular(env)
1469 if self.inverse_name:
1470 # link self to its inverse field and vice-versa
1471 invf = env[self.comodel_name]._fields[self.inverse_name]
1472 self.inverse_field = invf
1473 invf.inverse_field = self
1475 _description_relation_field = property(attrgetter('inverse_name'))
1477 _column_fields_id = property(attrgetter('inverse_name'))
1478 _column_auto_join = property(attrgetter('auto_join'))
1479 _column_limit = property(attrgetter('limit'))
1482 class Many2many(_RelationalMulti):
1483 """ Many2many field; the value of such a field is the recordset.
1485 :param comodel_name: name of the target model (string)
1487 The attribute `comodel_name` is mandatory except in the case of related
1488 fields or field extensions.
1490 :param relation: optional name of the table that stores the relation in
1491 the database (string)
1493 :param column1: optional name of the column referring to "these" records
1494 in the table `relation` (string)
1496 :param column2: optional name of the column referring to "those" records
1497 in the table `relation` (string)
1499 The attributes `relation`, `column1` and `column2` are optional. If not
1500 given, names are automatically generated from model names, provided
1501 `model_name` and `comodel_name` are different!
1503 :param domain: an optional domain to set on candidate values on the
1504 client side (domain or string)
1506 :param context: an optional context to use on the client side when
1507 handling that field (dictionary)
1509 :param limit: optional limit to use upon read (integer)
1513 relation = None # name of table
1514 column1 = None # column of table referring to model
1515 column2 = None # column of table referring to comodel
1516 limit = None # optional limit to use upon read
1518 def __init__(self, comodel_name=None, relation=None, column1=None, column2=None,
1519 string=None, **kwargs):
1520 super(Many2many, self).__init__(
1521 comodel_name=comodel_name,
1529 def _setup_regular(self, env):
1530 super(Many2many, self)._setup_regular(env)
1532 if self.store and not self.relation:
1533 model = env[self.model_name]
1534 column = model._columns[self.name]
1535 if not isinstance(column, fields.function):
1536 self.relation, self.column1, self.column2 = column._sql_names(model)
1539 m2m = env.registry._m2m
1540 # if inverse field has already been setup, it is present in m2m
1541 invf = m2m.get((self.relation, self.column2, self.column1))
1543 self.inverse_field = invf
1544 invf.inverse_field = self
1546 # add self in m2m, so that its inverse field can find it
1547 m2m[(self.relation, self.column1, self.column2)] = self
1549 _column_rel = property(attrgetter('relation'))
1550 _column_id1 = property(attrgetter('column1'))
1551 _column_id2 = property(attrgetter('column2'))
1552 _column_limit = property(attrgetter('limit'))
1556 """ Special case for field 'id'. """
1560 def __init__(self, string=None, **kwargs):
1561 super(Id, self).__init__(type='integer', string=string, **kwargs)
1563 def to_column(self):
1564 return fields.integer('ID')
1566 def __get__(self, record, owner):
1568 return self # the field is accessed through the class owner
1571 return record.ensure_one()._ids[0]
1573 def __set__(self, record, value):
1574 raise TypeError("field 'id' cannot be assigned")
1577 # imported here to avoid dependency cycle issues
1578 from openerp import SUPERUSER_ID
1579 from .exceptions import Warning, MissingError
1580 from .models import BaseModel, MAGIC_COLUMNS
1581 from .osv import fields