[IMP] models: do not use compute methods to determine default values anymore
[odoo/odoo.git] / openerp / models.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
6 #
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.
11 #
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.
16 #
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/>.
19 #
20 ##############################################################################
21
22
23 """
24     Object Relational Mapping module:
25      * Hierarchical structure
26      * Constraints consistency and validation
27      * Object metadata depends on its status
28      * Optimised processing by complex query (multiple actions at once)
29      * Default field values
30      * Permissions optimisation
31      * Persistant object: DB postgresql
32      * Data conversion
33      * Multi-level caching system
34      * Two different inheritance mechanisms
35      * Rich set of field types:
36           - classical (varchar, integer, boolean, ...)
37           - relational (one2many, many2one, many2many)
38           - functional
39
40 """
41
42 import datetime
43 import functools
44 import itertools
45 import logging
46 import operator
47 import pickle
48 import pytz
49 import re
50 import time
51 from collections import defaultdict, MutableMapping
52 from inspect import getmembers
53
54 import babel.dates
55 import dateutil.relativedelta
56 import psycopg2
57 from lxml import etree
58
59 import openerp
60 from . import SUPERUSER_ID
61 from . import api
62 from . import tools
63 from .api import Environment
64 from .exceptions import except_orm, AccessError, MissingError, ValidationError
65 from .osv import fields
66 from .osv.query import Query
67 from .tools import lazy_property, ormcache
68 from .tools.config import config
69 from .tools.misc import CountingStream, DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT
70 from .tools.safe_eval import safe_eval as eval
71 from .tools.translate import _
72
73 _logger = logging.getLogger(__name__)
74 _schema = logging.getLogger(__name__ + '.schema')
75
76 regex_order = re.compile('^( *([a-z0-9:_]+|"[a-z0-9:_]+")( *desc| *asc)?( *, *|))+$', re.I)
77 regex_object_name = re.compile(r'^[a-z0-9_.]+$')
78 onchange_v7 = re.compile(r"^(\w+)\((.*)\)$")
79
80 AUTOINIT_RECALCULATE_STORED_FIELDS = 1000
81
82
83 def check_object_name(name):
84     """ Check if the given name is a valid openerp object name.
85
86         The _name attribute in osv and osv_memory object is subject to
87         some restrictions. This function returns True or False whether
88         the given name is allowed or not.
89
90         TODO: this is an approximation. The goal in this approximation
91         is to disallow uppercase characters (in some places, we quote
92         table/column names and in other not, which leads to this kind
93         of errors:
94
95             psycopg2.ProgrammingError: relation "xxx" does not exist).
96
97         The same restriction should apply to both osv and osv_memory
98         objects for consistency.
99
100     """
101     if regex_object_name.match(name) is None:
102         return False
103     return True
104
105 def raise_on_invalid_object_name(name):
106     if not check_object_name(name):
107         msg = "The _name attribute %s is not valid." % name
108         _logger.error(msg)
109         raise except_orm('ValueError', msg)
110
111 POSTGRES_CONFDELTYPES = {
112     'RESTRICT': 'r',
113     'NO ACTION': 'a',
114     'CASCADE': 'c',
115     'SET NULL': 'n',
116     'SET DEFAULT': 'd',
117 }
118
119 def intersect(la, lb):
120     return filter(lambda x: x in lb, la)
121
122 def same_name(f, g):
123     """ Test whether functions `f` and `g` are identical or have the same name """
124     return f == g or getattr(f, '__name__', 0) == getattr(g, '__name__', 1)
125
126 def fix_import_export_id_paths(fieldname):
127     """
128     Fixes the id fields in import and exports, and splits field paths
129     on '/'.
130
131     :param str fieldname: name of the field to import/export
132     :return: split field name
133     :rtype: list of str
134     """
135     fixed_db_id = re.sub(r'([^/])\.id', r'\1/.id', fieldname)
136     fixed_external_id = re.sub(r'([^/]):id', r'\1/id', fixed_db_id)
137     return fixed_external_id.split('/')
138
139 def pg_varchar(size=0):
140     """ Returns the VARCHAR declaration for the provided size:
141
142     * If no size (or an empty or negative size is provided) return an
143       'infinite' VARCHAR
144     * Otherwise return a VARCHAR(n)
145
146     :type int size: varchar size, optional
147     :rtype: str
148     """
149     if size:
150         if not isinstance(size, int):
151             raise TypeError("VARCHAR parameter should be an int, got %s"
152                             % type(size))
153         if size > 0:
154             return 'VARCHAR(%d)' % size
155     return 'VARCHAR'
156
157 FIELDS_TO_PGTYPES = {
158     fields.boolean: 'bool',
159     fields.integer: 'int4',
160     fields.text: 'text',
161     fields.html: 'text',
162     fields.date: 'date',
163     fields.datetime: 'timestamp',
164     fields.binary: 'bytea',
165     fields.many2one: 'int4',
166     fields.serialized: 'text',
167 }
168
169 def get_pg_type(f, type_override=None):
170     """
171     :param fields._column f: field to get a Postgres type for
172     :param type type_override: use the provided type for dispatching instead of the field's own type
173     :returns: (postgres_identification_type, postgres_type_specification)
174     :rtype: (str, str)
175     """
176     field_type = type_override or type(f)
177
178     if field_type in FIELDS_TO_PGTYPES:
179         pg_type =  (FIELDS_TO_PGTYPES[field_type], FIELDS_TO_PGTYPES[field_type])
180     elif issubclass(field_type, fields.float):
181         if f.digits:
182             pg_type = ('numeric', 'NUMERIC')
183         else:
184             pg_type = ('float8', 'DOUBLE PRECISION')
185     elif issubclass(field_type, (fields.char, fields.reference)):
186         pg_type = ('varchar', pg_varchar(f.size))
187     elif issubclass(field_type, fields.selection):
188         if (isinstance(f.selection, list) and isinstance(f.selection[0][0], int))\
189                 or getattr(f, 'size', None) == -1:
190             pg_type = ('int4', 'INTEGER')
191         else:
192             pg_type = ('varchar', pg_varchar(getattr(f, 'size', None)))
193     elif issubclass(field_type, fields.function):
194         if f._type == 'selection':
195             pg_type = ('varchar', pg_varchar())
196         else:
197             pg_type = get_pg_type(f, getattr(fields, f._type))
198     else:
199         _logger.warning('%s type not supported!', field_type)
200         pg_type = None
201
202     return pg_type
203
204
205 class MetaModel(api.Meta):
206     """ Metaclass for the models.
207
208     This class is used as the metaclass for the class :class:`BaseModel` to
209     discover the models defined in a module (without instanciating them).
210     If the automatic discovery is not needed, it is possible to set the model's
211     ``_register`` attribute to False.
212
213     """
214
215     module_to_models = {}
216
217     def __init__(self, name, bases, attrs):
218         if not self._register:
219             self._register = True
220             super(MetaModel, self).__init__(name, bases, attrs)
221             return
222
223         if not hasattr(self, '_module'):
224             # The (OpenERP) module name can be in the `openerp.addons` namespace
225             # or not.  For instance, module `sale` can be imported as
226             # `openerp.addons.sale` (the right way) or `sale` (for backward
227             # compatibility).
228             module_parts = self.__module__.split('.')
229             if len(module_parts) > 2 and module_parts[:2] == ['openerp', 'addons']:
230                 module_name = self.__module__.split('.')[2]
231             else:
232                 module_name = self.__module__.split('.')[0]
233             self._module = module_name
234
235         # Remember which models to instanciate for this module.
236         if not self._custom:
237             self.module_to_models.setdefault(self._module, []).append(self)
238
239         # transform columns into new-style fields (enables field inheritance)
240         for name, column in self._columns.iteritems():
241             if name in self.__dict__:
242                 _logger.warning("Field %r erasing an existing value", name)
243             setattr(self, name, column.to_field())
244
245
246 class NewId(object):
247     """ Pseudo-ids for new records. """
248     def __nonzero__(self):
249         return False
250
251 IdType = (int, long, basestring, NewId)
252
253
254 # maximum number of prefetched records
255 PREFETCH_MAX = 200
256
257 # special columns automatically created by the ORM
258 LOG_ACCESS_COLUMNS = ['create_uid', 'create_date', 'write_uid', 'write_date']
259 MAGIC_COLUMNS = ['id'] + LOG_ACCESS_COLUMNS
260
261 class BaseModel(object):
262     """ Base class for OpenERP models.
263
264     OpenERP models are created by inheriting from this class' subclasses:
265
266     *   :class:`Model` for regular database-persisted models
267
268     *   :class:`TransientModel` for temporary data, stored in the database but
269         automatically vaccuumed every so often
270
271     *   :class:`AbstractModel` for abstract super classes meant to be shared by
272         multiple inheriting model
273
274     The system automatically instantiates every model once per database. Those
275     instances represent the available models on each database, and depend on
276     which modules are installed on that database. The actual class of each
277     instance is built from the Python classes that create and inherit from the
278     corresponding model.
279
280     Every model instance is a "recordset", i.e., an ordered collection of
281     records of the model. Recordsets are returned by methods like
282     :meth:`~.browse`, :meth:`~.search`, or field accesses. Records have no
283     explicit representation: a record is represented as a recordset of one
284     record.
285
286     To create a class that should not be instantiated, the _register class
287     attribute may be set to False.
288     """
289     __metaclass__ = MetaModel
290     _auto = True # create database backend
291     _register = False # Set to false if the model shouldn't be automatically discovered.
292     _name = None
293     _columns = {}
294     _constraints = []
295     _custom = False
296     _defaults = {}
297     _rec_name = None
298     _parent_name = 'parent_id'
299     _parent_store = False
300     _parent_order = False
301     _date_name = 'date'
302     _order = 'id'
303     _sequence = None
304     _description = None
305     _needaction = False
306     _translate = True # set to False to disable translations export for this model
307
308     # dict of {field:method}, with method returning the (name_get of records, {id: fold})
309     # to include in the _read_group, if grouped on this field
310     _group_by_full = {}
311
312     # Transience
313     _transient = False # True in a TransientModel
314
315     # structure:
316     #  { 'parent_model': 'm2o_field', ... }
317     _inherits = {}
318
319     # Mapping from inherits'd field name to triple (m, r, f, n) where m is the
320     # model from which it is inherits'd, r is the (local) field towards m, f
321     # is the _column object itself, and n is the original (i.e. top-most)
322     # parent model.
323     # Example:
324     #  { 'field_name': ('parent_model', 'm2o_field_to_reach_parent',
325     #                   field_column_obj, origina_parent_model), ... }
326     _inherit_fields = {}
327
328     # Mapping field name/column_info object
329     # This is similar to _inherit_fields but:
330     # 1. includes self fields,
331     # 2. uses column_info instead of a triple.
332     _all_columns = {}
333
334     _table = None
335     _log_create = False
336     _sql_constraints = []
337
338     # model dependencies, for models backed up by sql views:
339     # {model_name: field_names, ...}
340     _depends = {}
341
342     CONCURRENCY_CHECK_FIELD = '__last_update'
343
344     def log(self, cr, uid, id, message, secondary=False, context=None):
345         return _logger.warning("log() is deprecated. Please use OpenChatter notification system instead of the res.log mechanism.")
346
347     def view_init(self, cr, uid, fields_list, context=None):
348         """Override this method to do specific things when a view on the object is opened."""
349         pass
350
351     def _field_create(self, cr, context=None):
352         """ Create entries in ir_model_fields for all the model's fields.
353
354         If necessary, also create an entry in ir_model, and if called from the
355         modules loading scheme (by receiving 'module' in the context), also
356         create entries in ir_model_data (for the model and the fields).
357
358         - create an entry in ir_model (if there is not already one),
359         - create an entry in ir_model_data (if there is not already one, and if
360           'module' is in the context),
361         - update ir_model_fields with the fields found in _columns
362           (TODO there is some redundancy as _columns is updated from
363           ir_model_fields in __init__).
364
365         """
366         if context is None:
367             context = {}
368         cr.execute("SELECT id FROM ir_model WHERE model=%s", (self._name,))
369         if not cr.rowcount:
370             cr.execute('SELECT nextval(%s)', ('ir_model_id_seq',))
371             model_id = cr.fetchone()[0]
372             cr.execute("INSERT INTO ir_model (id,model, name, info,state) VALUES (%s, %s, %s, %s, %s)", (model_id, self._name, self._description, self.__doc__, 'base'))
373         else:
374             model_id = cr.fetchone()[0]
375         if 'module' in context:
376             name_id = 'model_'+self._name.replace('.', '_')
377             cr.execute('select * from ir_model_data where name=%s and module=%s', (name_id, context['module']))
378             if not cr.rowcount:
379                 cr.execute("INSERT INTO ir_model_data (name,date_init,date_update,module,model,res_id) VALUES (%s, (now() at time zone 'UTC'), (now() at time zone 'UTC'), %s, %s, %s)", \
380                     (name_id, context['module'], 'ir.model', model_id)
381                 )
382
383         cr.execute("SELECT * FROM ir_model_fields WHERE model=%s", (self._name,))
384         cols = {}
385         for rec in cr.dictfetchall():
386             cols[rec['name']] = rec
387
388         ir_model_fields_obj = self.pool.get('ir.model.fields')
389
390         # sparse field should be created at the end, as it depends on its serialized field already existing
391         model_fields = sorted(self._columns.items(), key=lambda x: 1 if x[1]._type == 'sparse' else 0)
392         for (k, f) in model_fields:
393             vals = {
394                 'model_id': model_id,
395                 'model': self._name,
396                 'name': k,
397                 'field_description': f.string,
398                 'ttype': f._type,
399                 'relation': f._obj or '',
400                 'select_level': tools.ustr(int(f.select)),
401                 'readonly': (f.readonly and 1) or 0,
402                 'required': (f.required and 1) or 0,
403                 'selectable': (f.selectable and 1) or 0,
404                 'translate': (f.translate and 1) or 0,
405                 'relation_field': f._fields_id if isinstance(f, fields.one2many) else '',
406                 'serialization_field_id': None,
407             }
408             if getattr(f, 'serialization_field', None):
409                 # resolve link to serialization_field if specified by name
410                 serialization_field_id = ir_model_fields_obj.search(cr, SUPERUSER_ID, [('model','=',vals['model']), ('name', '=', f.serialization_field)])
411                 if not serialization_field_id:
412                     raise except_orm(_('Error'), _("Serialization field `%s` not found for sparse field `%s`!") % (f.serialization_field, k))
413                 vals['serialization_field_id'] = serialization_field_id[0]
414
415             # When its a custom field,it does not contain f.select
416             if context.get('field_state', 'base') == 'manual':
417                 if context.get('field_name', '') == k:
418                     vals['select_level'] = context.get('select', '0')
419                 #setting value to let the problem NOT occur next time
420                 elif k in cols:
421                     vals['select_level'] = cols[k]['select_level']
422
423             if k not in cols:
424                 cr.execute('select nextval(%s)', ('ir_model_fields_id_seq',))
425                 id = cr.fetchone()[0]
426                 vals['id'] = id
427                 cr.execute("""INSERT INTO ir_model_fields (
428                     id, model_id, model, name, field_description, ttype,
429                     relation,state,select_level,relation_field, translate, serialization_field_id
430                 ) VALUES (
431                     %s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s
432                 )""", (
433                     id, vals['model_id'], vals['model'], vals['name'], vals['field_description'], vals['ttype'],
434                      vals['relation'], 'base',
435                     vals['select_level'], vals['relation_field'], bool(vals['translate']), vals['serialization_field_id']
436                 ))
437                 if 'module' in context:
438                     name1 = 'field_' + self._table + '_' + k
439                     cr.execute("select name from ir_model_data where name=%s", (name1,))
440                     if cr.fetchone():
441                         name1 = name1 + "_" + str(id)
442                     cr.execute("INSERT INTO ir_model_data (name,date_init,date_update,module,model,res_id) VALUES (%s, (now() at time zone 'UTC'), (now() at time zone 'UTC'), %s, %s, %s)", \
443                         (name1, context['module'], 'ir.model.fields', id)
444                     )
445             else:
446                 for key, val in vals.items():
447                     if cols[k][key] != vals[key]:
448                         cr.execute('update ir_model_fields set field_description=%s where model=%s and name=%s', (vals['field_description'], vals['model'], vals['name']))
449                         cr.execute("""UPDATE ir_model_fields SET
450                             model_id=%s, field_description=%s, ttype=%s, relation=%s,
451                             select_level=%s, readonly=%s ,required=%s, selectable=%s, relation_field=%s, translate=%s, serialization_field_id=%s
452                         WHERE
453                             model=%s AND name=%s""", (
454                                 vals['model_id'], vals['field_description'], vals['ttype'],
455                                 vals['relation'],
456                                 vals['select_level'], bool(vals['readonly']), bool(vals['required']), bool(vals['selectable']), vals['relation_field'], bool(vals['translate']), vals['serialization_field_id'], vals['model'], vals['name']
457                             ))
458                         break
459         self.invalidate_cache(cr, SUPERUSER_ID)
460
461     @classmethod
462     def _add_field(cls, name, field):
463         """ Add the given `field` under the given `name` in the class """
464         field.set_class_name(cls, name)
465
466         # add field in _fields (for reflection)
467         cls._fields[name] = field
468
469         # add field as an attribute, unless another kind of value already exists
470         if isinstance(getattr(cls, name, field), Field):
471             setattr(cls, name, field)
472         else:
473             _logger.warning("In model %r, member %r is not a field", cls._name, name)
474
475         if field.store:
476             cls._columns[name] = field.to_column()
477         else:
478             # remove potential column that may be overridden by field
479             cls._columns.pop(name, None)
480
481     @classmethod
482     def _pop_field(cls, name):
483         """ Remove the field with the given `name` from the model.
484             This method should only be used for manual fields.
485         """
486         field = cls._fields.pop(name)
487         cls._columns.pop(name, None)
488         cls._all_columns.pop(name, None)
489         if hasattr(cls, name):
490             delattr(cls, name)
491         return field
492
493     @classmethod
494     def _add_magic_fields(cls):
495         """ Introduce magic fields on the current class
496
497         * id is a "normal" field (with a specific getter)
498         * create_uid, create_date, write_uid and write_date have become
499           "normal" fields
500         * $CONCURRENCY_CHECK_FIELD is a computed field with its computing
501           method defined dynamically. Uses ``str(datetime.datetime.utcnow())``
502           to get the same structure as the previous
503           ``(now() at time zone 'UTC')::timestamp``::
504
505               # select (now() at time zone 'UTC')::timestamp;
506                         timezone
507               ----------------------------
508                2013-06-18 08:30:37.292809
509
510               >>> str(datetime.datetime.utcnow())
511               '2013-06-18 08:31:32.821177'
512         """
513         def add(name, field):
514             """ add `field` with the given `name` if it does not exist yet """
515             if name not in cls._columns and name not in cls._fields:
516                 cls._add_field(name, field)
517
518         # cyclic import
519         from . import fields
520
521         # this field 'id' must override any other column or field
522         cls._add_field('id', fields.Id(automatic=True))
523
524         add('display_name', fields.Char(string='Display Name', automatic=True,
525             compute='_compute_display_name'))
526
527         if cls._log_access:
528             add('create_uid', fields.Many2one('res.users', string='Created by', automatic=True))
529             add('create_date', fields.Datetime(string='Created on', automatic=True))
530             add('write_uid', fields.Many2one('res.users', string='Last Updated by', automatic=True))
531             add('write_date', fields.Datetime(string='Last Updated on', automatic=True))
532             last_modified_name = 'compute_concurrency_field_with_access'
533         else:
534             last_modified_name = 'compute_concurrency_field'
535
536         # this field must override any other column or field
537         cls._add_field(cls.CONCURRENCY_CHECK_FIELD, fields.Datetime(
538             string='Last Modified on', compute=last_modified_name, automatic=True))
539
540     @api.one
541     def compute_concurrency_field(self):
542         self[self.CONCURRENCY_CHECK_FIELD] = \
543             datetime.datetime.utcnow().strftime(DEFAULT_SERVER_DATETIME_FORMAT)
544
545     @api.one
546     @api.depends('create_date', 'write_date')
547     def compute_concurrency_field_with_access(self):
548         self[self.CONCURRENCY_CHECK_FIELD] = \
549             self.write_date or self.create_date or \
550             datetime.datetime.utcnow().strftime(DEFAULT_SERVER_DATETIME_FORMAT)
551
552     #
553     # Goal: try to apply inheritance at the instanciation level and
554     #       put objects in the pool var
555     #
556     @classmethod
557     def _build_model(cls, pool, cr):
558         """ Instanciate a given model.
559
560         This class method instanciates the class of some model (i.e. a class
561         deriving from osv or osv_memory). The class might be the class passed
562         in argument or, if it inherits from another class, a class constructed
563         by combining the two classes.
564
565         """
566
567         # IMPORTANT: the registry contains an instance for each model. The class
568         # of each model carries inferred metadata that is shared among the
569         # model's instances for this registry, but not among registries. Hence
570         # we cannot use that "registry class" for combining model classes by
571         # inheritance, since it confuses the metadata inference process.
572
573         # Keep links to non-inherited constraints in cls; this is useful for
574         # instance when exporting translations
575         cls._local_constraints = cls.__dict__.get('_constraints', [])
576         cls._local_sql_constraints = cls.__dict__.get('_sql_constraints', [])
577
578         # determine inherited models
579         parents = getattr(cls, '_inherit', [])
580         parents = [parents] if isinstance(parents, basestring) else (parents or [])
581
582         # determine the model's name
583         name = cls._name or (len(parents) == 1 and parents[0]) or cls.__name__
584
585         # determine the module that introduced the model
586         original_module = pool[name]._original_module if name in parents else cls._module
587
588         # build the class hierarchy for the model
589         for parent in parents:
590             if parent not in pool:
591                 raise TypeError('The model "%s" specifies an unexisting parent class "%s"\n'
592                     'You may need to add a dependency on the parent class\' module.' % (name, parent))
593             parent_model = pool[parent]
594
595             # do no use the class of parent_model, since that class contains
596             # inferred metadata; use its ancestor instead
597             parent_class = type(parent_model).__base__
598
599             # don't inherit custom fields
600             columns = dict((key, val)
601                 for key, val in parent_class._columns.iteritems()
602                 if not val.manual
603             )
604             columns.update(cls._columns)
605
606             inherits = dict(parent_class._inherits)
607             inherits.update(cls._inherits)
608
609             depends = dict(parent_class._depends)
610             for m, fs in cls._depends.iteritems():
611                 depends[m] = depends.get(m, []) + fs
612
613             old_constraints = parent_class._constraints
614             new_constraints = cls._constraints
615             # filter out from old_constraints the ones overridden by a
616             # constraint with the same function name in new_constraints
617             constraints = new_constraints + [oldc
618                 for oldc in old_constraints
619                 if not any(newc[2] == oldc[2] and same_name(newc[0], oldc[0])
620                            for newc in new_constraints)
621             ]
622
623             sql_constraints = cls._sql_constraints + \
624                               parent_class._sql_constraints
625
626             attrs = {
627                 '_name': name,
628                 '_register': False,
629                 '_columns': columns,
630                 '_inherits': inherits,
631                 '_depends': depends,
632                 '_constraints': constraints,
633                 '_sql_constraints': sql_constraints,
634             }
635             cls = type(name, (cls, parent_class), attrs)
636
637         # introduce the "registry class" of the model;
638         # duplicate some attributes so that the ORM can modify them
639         attrs = {
640             '_name': name,
641             '_register': False,
642             '_columns': dict(cls._columns),
643             '_defaults': {},            # filled by Field._determine_default()
644             '_inherits': dict(cls._inherits),
645             '_depends': dict(cls._depends),
646             '_constraints': list(cls._constraints),
647             '_sql_constraints': list(cls._sql_constraints),
648             '_original_module': original_module,
649         }
650         cls = type(cls._name, (cls,), attrs)
651
652         # instantiate the model, and initialize it
653         model = object.__new__(cls)
654         model.__init__(pool, cr)
655         return model
656
657     @classmethod
658     def _init_function_fields(cls, pool, cr):
659         # initialize the list of non-stored function fields for this model
660         pool._pure_function_fields[cls._name] = []
661
662         # process store of low-level function fields
663         for fname, column in cls._columns.iteritems():
664             if hasattr(column, 'digits_change'):
665                 column.digits_change(cr)
666             # filter out existing store about this field
667             pool._store_function[cls._name] = [
668                 stored
669                 for stored in pool._store_function.get(cls._name, [])
670                 if (stored[0], stored[1]) != (cls._name, fname)
671             ]
672             if not isinstance(column, fields.function):
673                 continue
674             if not column.store:
675                 # register it on the pool for invalidation
676                 pool._pure_function_fields[cls._name].append(fname)
677                 continue
678             # process store parameter
679             store = column.store
680             if store is True:
681                 get_ids = lambda self, cr, uid, ids, c={}: ids
682                 store = {cls._name: (get_ids, None, column.priority, None)}
683             for model, spec in store.iteritems():
684                 if len(spec) == 4:
685                     (fnct, fields2, order, length) = spec
686                 elif len(spec) == 3:
687                     (fnct, fields2, order) = spec
688                     length = None
689                 else:
690                     raise except_orm('Error',
691                         ('Invalid function definition %s in object %s !\nYou must use the definition: store={object:(fnct, fields, priority, time length)}.' % (fname, cls._name)))
692                 pool._store_function.setdefault(model, [])
693                 t = (cls._name, fname, fnct, tuple(fields2) if fields2 else None, order, length)
694                 if t not in pool._store_function[model]:
695                     pool._store_function[model].append(t)
696                     pool._store_function[model].sort(key=lambda x: x[4])
697
698     @classmethod
699     def _init_manual_fields(cls, pool, cr):
700         # Check whether the query is already done
701         if pool.fields_by_model is not None:
702             manual_fields = pool.fields_by_model.get(cls._name, [])
703         else:
704             cr.execute('SELECT * FROM ir_model_fields WHERE model=%s AND state=%s', (cls._name, 'manual'))
705             manual_fields = cr.dictfetchall()
706
707         for field in manual_fields:
708             if field['name'] in cls._columns:
709                 continue
710             attrs = {
711                 'string': field['field_description'],
712                 'required': bool(field['required']),
713                 'readonly': bool(field['readonly']),
714                 'domain': eval(field['domain']) if field['domain'] else None,
715                 'size': field['size'] or None,
716                 'ondelete': field['on_delete'],
717                 'translate': (field['translate']),
718                 'manual': True,
719                 '_prefetch': False,
720                 #'select': int(field['select_level'])
721             }
722             if field['serialization_field_id']:
723                 cr.execute('SELECT name FROM ir_model_fields WHERE id=%s', (field['serialization_field_id'],))
724                 attrs.update({'serialization_field': cr.fetchone()[0], 'type': field['ttype']})
725                 if field['ttype'] in ['many2one', 'one2many', 'many2many']:
726                     attrs.update({'relation': field['relation']})
727                 cls._columns[field['name']] = fields.sparse(**attrs)
728             elif field['ttype'] == 'selection':
729                 cls._columns[field['name']] = fields.selection(eval(field['selection']), **attrs)
730             elif field['ttype'] == 'reference':
731                 cls._columns[field['name']] = fields.reference(selection=eval(field['selection']), **attrs)
732             elif field['ttype'] == 'many2one':
733                 cls._columns[field['name']] = fields.many2one(field['relation'], **attrs)
734             elif field['ttype'] == 'one2many':
735                 cls._columns[field['name']] = fields.one2many(field['relation'], field['relation_field'], **attrs)
736             elif field['ttype'] == 'many2many':
737                 _rel1 = field['relation'].replace('.', '_')
738                 _rel2 = field['model'].replace('.', '_')
739                 _rel_name = 'x_%s_%s_%s_rel' % (_rel1, _rel2, field['name'])
740                 cls._columns[field['name']] = fields.many2many(field['relation'], _rel_name, 'id1', 'id2', **attrs)
741             else:
742                 cls._columns[field['name']] = getattr(fields, field['ttype'])(**attrs)
743
744     @classmethod
745     def _init_constraints_onchanges(cls):
746         # store sql constraint error messages
747         for (key, _, msg) in cls._sql_constraints:
748             cls.pool._sql_error[cls._table + '_' + key] = msg
749
750         # collect constraint and onchange methods
751         cls._constraint_methods = []
752         cls._onchange_methods = defaultdict(list)
753         for attr, func in getmembers(cls, callable):
754             if hasattr(func, '_constrains'):
755                 if not all(name in cls._fields for name in func._constrains):
756                     _logger.warning("@constrains%r parameters must be field names", func._constrains)
757                 cls._constraint_methods.append(func)
758             if hasattr(func, '_onchange'):
759                 if not all(name in cls._fields for name in func._onchange):
760                     _logger.warning("@onchange%r parameters must be field names", func._onchange)
761                 for name in func._onchange:
762                     cls._onchange_methods[name].append(func)
763
764     def __new__(cls):
765         # In the past, this method was registering the model class in the server.
766         # This job is now done entirely by the metaclass MetaModel.
767         #
768         # Do not create an instance here.  Model instances are created by method
769         # _build_model().
770         return None
771
772     def __init__(self, pool, cr):
773         """ Initialize a model and make it part of the given registry.
774
775         - copy the stored fields' functions in the registry,
776         - retrieve custom fields and add them in the model,
777         - ensure there is a many2one for each _inherits'd parent,
778         - update the children's _columns,
779         - give a chance to each field to initialize itself.
780
781         """
782         cls = type(self)
783
784         # link the class to the registry, and update the registry
785         cls.pool = pool
786         cls._model = self              # backward compatibility
787         pool.add(cls._name, self)
788
789         # determine description, table, sequence and log_access
790         if not cls._description:
791             cls._description = cls._name
792         if not cls._table:
793             cls._table = cls._name.replace('.', '_')
794         if not cls._sequence:
795             cls._sequence = cls._table + '_id_seq'
796         if not hasattr(cls, '_log_access'):
797             # If _log_access is not specified, it is the same value as _auto.
798             cls._log_access = cls._auto
799
800         # Transience
801         if cls.is_transient():
802             cls._transient_check_count = 0
803             cls._transient_max_count = config.get('osv_memory_count_limit')
804             cls._transient_max_hours = config.get('osv_memory_age_limit')
805             assert cls._log_access, \
806                 "TransientModels must have log_access turned on, " \
807                 "in order to implement their access rights policy"
808
809         # retrieve new-style fields and duplicate them (to avoid clashes with
810         # inheritance between different models)
811         cls._fields = {}
812         for attr, field in getmembers(cls, Field.__instancecheck__):
813             if not field.inherited:
814                 cls._add_field(attr, field.new())
815
816         # introduce magic fields
817         cls._add_magic_fields()
818
819         # register stuff about low-level function fields and custom fields
820         cls._init_function_fields(pool, cr)
821         cls._init_manual_fields(pool, cr)
822
823         # process _inherits
824         cls._inherits_check()
825         cls._inherits_reload()
826
827         # register constraints and onchange methods
828         cls._init_constraints_onchanges()
829
830         # check defaults
831         for k in cls._defaults:
832             assert k in cls._fields, \
833                 "Model %s has a default for nonexiting field %s" % (cls._name, k)
834
835         # restart columns
836         for column in cls._columns.itervalues():
837             column.restart()
838
839         # validate rec_name
840         if cls._rec_name:
841             assert cls._rec_name in cls._fields, \
842                 "Invalid rec_name %s for model %s" % (cls._rec_name, cls._name)
843         elif 'name' in cls._fields:
844             cls._rec_name = 'name'
845
846         # prepare ormcache, which must be shared by all instances of the model
847         cls._ormcache = {}
848
849     @api.model
850     @ormcache()
851     def _is_an_ordinary_table(self):
852         self.env.cr.execute("""\
853             SELECT  1
854             FROM    pg_class
855             WHERE   relname = %s
856             AND     relkind = %s""", [self._table, 'r'])
857         return bool(self.env.cr.fetchone())
858
859     def __export_xml_id(self):
860         """ Return a valid xml_id for the record `self`. """
861         if not self._is_an_ordinary_table():
862             raise Exception(
863                 "You can not export the column ID of model %s, because the "
864                 "table %s is not an ordinary table."
865                 % (self._name, self._table))
866         ir_model_data = self.sudo().env['ir.model.data']
867         data = ir_model_data.search([('model', '=', self._name), ('res_id', '=', self.id)])
868         if data:
869             if data[0].module:
870                 return '%s.%s' % (data[0].module, data[0].name)
871             else:
872                 return data[0].name
873         else:
874             postfix = 0
875             name = '%s_%s' % (self._table, self.id)
876             while ir_model_data.search([('module', '=', '__export__'), ('name', '=', name)]):
877                 postfix += 1
878                 name = '%s_%s_%s' % (self._table, self.id, postfix)
879             ir_model_data.create({
880                 'model': self._name,
881                 'res_id': self.id,
882                 'module': '__export__',
883                 'name': name,
884             })
885             return '__export__.' + name
886
887     @api.multi
888     def __export_rows(self, fields):
889         """ Export fields of the records in `self`.
890
891             :param fields: list of lists of fields to traverse
892             :return: list of lists of corresponding values
893         """
894         lines = []
895         for record in self:
896             # main line of record, initially empty
897             current = [''] * len(fields)
898             lines.append(current)
899
900             # list of primary fields followed by secondary field(s)
901             primary_done = []
902
903             # process column by column
904             for i, path in enumerate(fields):
905                 if not path:
906                     continue
907
908                 name = path[0]
909                 if name in primary_done:
910                     continue
911
912                 if name == '.id':
913                     current[i] = str(record.id)
914                 elif name == 'id':
915                     current[i] = record.__export_xml_id()
916                 else:
917                     field = record._fields[name]
918                     value = record[name]
919
920                     # this part could be simpler, but it has to be done this way
921                     # in order to reproduce the former behavior
922                     if not isinstance(value, BaseModel):
923                         current[i] = field.convert_to_export(value, self.env)
924                     else:
925                         primary_done.append(name)
926
927                         # This is a special case, its strange behavior is intended!
928                         if field.type == 'many2many' and len(path) > 1 and path[1] == 'id':
929                             xml_ids = [r.__export_xml_id() for r in value]
930                             current[i] = ','.join(xml_ids) or False
931                             continue
932
933                         # recursively export the fields that follow name
934                         fields2 = [(p[1:] if p and p[0] == name else []) for p in fields]
935                         lines2 = value.__export_rows(fields2)
936                         if lines2:
937                             # merge first line with record's main line
938                             for j, val in enumerate(lines2[0]):
939                                 if val:
940                                     current[j] = val
941                             # check value of current field
942                             if not current[i]:
943                                 # assign xml_ids, and forget about remaining lines
944                                 xml_ids = [item[1] for item in value.name_get()]
945                                 current[i] = ','.join(xml_ids)
946                             else:
947                                 # append the other lines at the end
948                                 lines += lines2[1:]
949                         else:
950                             current[i] = False
951
952         return lines
953
954     @api.multi
955     def export_data(self, fields_to_export, raw_data=False):
956         """ Export fields for selected objects
957
958             :param fields_to_export: list of fields
959             :param raw_data: True to return value in native Python type
960             :rtype: dictionary with a *datas* matrix
961
962             This method is used when exporting data via client menu
963         """
964         fields_to_export = map(fix_import_export_id_paths, fields_to_export)
965         if raw_data:
966             self = self.with_context(export_raw_data=True)
967         return {'datas': self.__export_rows(fields_to_export)}
968
969     def import_data(self, cr, uid, fields, datas, mode='init', current_module='', noupdate=False, context=None, filename=None):
970         """
971         .. deprecated:: 7.0
972             Use :meth:`~load` instead
973
974         Import given data in given module
975
976         This method is used when importing data via client menu.
977
978         Example of fields to import for a sale.order::
979
980             .id,                         (=database_id)
981             partner_id,                  (=name_search)
982             order_line/.id,              (=database_id)
983             order_line/name,
984             order_line/product_id/id,    (=xml id)
985             order_line/price_unit,
986             order_line/product_uom_qty,
987             order_line/product_uom/id    (=xml_id)
988
989         This method returns a 4-tuple with the following structure::
990
991             (return_code, errored_resource, error_message, unused)
992
993         * The first item is a return code, it is ``-1`` in case of
994           import error, or the last imported row number in case of success
995         * The second item contains the record data dict that failed to import
996           in case of error, otherwise it's 0
997         * The third item contains an error message string in case of error,
998           otherwise it's 0
999         * The last item is currently unused, with no specific semantics
1000
1001         :param fields: list of fields to import
1002         :param datas: data to import
1003         :param mode: 'init' or 'update' for record creation
1004         :param current_module: module name
1005         :param noupdate: flag for record creation
1006         :param filename: optional file to store partial import state for recovery
1007         :returns: 4-tuple in the form (return_code, errored_resource, error_message, unused)
1008         :rtype: (int, dict or 0, str or 0, str or 0)
1009         """
1010         context = dict(context) if context is not None else {}
1011         context['_import_current_module'] = current_module
1012
1013         fields = map(fix_import_export_id_paths, fields)
1014         ir_model_data_obj = self.pool.get('ir.model.data')
1015
1016         def log(m):
1017             if m['type'] == 'error':
1018                 raise Exception(m['message'])
1019
1020         if config.get('import_partial') and filename:
1021             with open(config.get('import_partial'), 'rb') as partial_import_file:
1022                 data = pickle.load(partial_import_file)
1023                 position = data.get(filename, 0)
1024
1025         position = 0
1026         try:
1027             for res_id, xml_id, res, info in self._convert_records(cr, uid,
1028                             self._extract_records(cr, uid, fields, datas,
1029                                                   context=context, log=log),
1030                             context=context, log=log):
1031                 ir_model_data_obj._update(cr, uid, self._name,
1032                      current_module, res, mode=mode, xml_id=xml_id,
1033                      noupdate=noupdate, res_id=res_id, context=context)
1034                 position = info.get('rows', {}).get('to', 0) + 1
1035                 if config.get('import_partial') and filename and (not (position%100)):
1036                     with open(config.get('import_partial'), 'rb') as partial_import:
1037                         data = pickle.load(partial_import)
1038                     data[filename] = position
1039                     with open(config.get('import_partial'), 'wb') as partial_import:
1040                         pickle.dump(data, partial_import)
1041                     if context.get('defer_parent_store_computation'):
1042                         self._parent_store_compute(cr)
1043                     cr.commit()
1044         except Exception, e:
1045             cr.rollback()
1046             return -1, {}, 'Line %d : %s' % (position + 1, tools.ustr(e)), ''
1047
1048         if context.get('defer_parent_store_computation'):
1049             self._parent_store_compute(cr)
1050         return position, 0, 0, 0
1051
1052     def load(self, cr, uid, fields, data, context=None):
1053         """
1054         Attempts to load the data matrix, and returns a list of ids (or
1055         ``False`` if there was an error and no id could be generated) and a
1056         list of messages.
1057
1058         The ids are those of the records created and saved (in database), in
1059         the same order they were extracted from the file. They can be passed
1060         directly to :meth:`~read`
1061
1062         :param fields: list of fields to import, at the same index as the corresponding data
1063         :type fields: list(str)
1064         :param data: row-major matrix of data to import
1065         :type data: list(list(str))
1066         :param dict context:
1067         :returns: {ids: list(int)|False, messages: [Message]}
1068         """
1069         cr.execute('SAVEPOINT model_load')
1070         messages = []
1071
1072         fields = map(fix_import_export_id_paths, fields)
1073         ModelData = self.pool['ir.model.data'].clear_caches()
1074
1075         fg = self.fields_get(cr, uid, context=context)
1076
1077         mode = 'init'
1078         current_module = ''
1079         noupdate = False
1080
1081         ids = []
1082         for id, xid, record, info in self._convert_records(cr, uid,
1083                 self._extract_records(cr, uid, fields, data,
1084                                       context=context, log=messages.append),
1085                 context=context, log=messages.append):
1086             try:
1087                 cr.execute('SAVEPOINT model_load_save')
1088             except psycopg2.InternalError, e:
1089                 # broken transaction, exit and hope the source error was
1090                 # already logged
1091                 if not any(message['type'] == 'error' for message in messages):
1092                     messages.append(dict(info, type='error',message=
1093                         u"Unknown database error: '%s'" % e))
1094                 break
1095             try:
1096                 ids.append(ModelData._update(cr, uid, self._name,
1097                      current_module, record, mode=mode, xml_id=xid,
1098                      noupdate=noupdate, res_id=id, context=context))
1099                 cr.execute('RELEASE SAVEPOINT model_load_save')
1100             except psycopg2.Warning, e:
1101                 messages.append(dict(info, type='warning', message=str(e)))
1102                 cr.execute('ROLLBACK TO SAVEPOINT model_load_save')
1103             except psycopg2.Error, e:
1104                 messages.append(dict(
1105                     info, type='error',
1106                     **PGERROR_TO_OE[e.pgcode](self, fg, info, e)))
1107                 # Failed to write, log to messages, rollback savepoint (to
1108                 # avoid broken transaction) and keep going
1109                 cr.execute('ROLLBACK TO SAVEPOINT model_load_save')
1110             except Exception, e:
1111                 message = (_('Unknown error during import:') +
1112                            ' %s: %s' % (type(e), unicode(e)))
1113                 moreinfo = _('Resolve other errors first')
1114                 messages.append(dict(info, type='error',
1115                                      message=message,
1116                                      moreinfo=moreinfo))
1117                 # Failed for some reason, perhaps due to invalid data supplied,
1118                 # rollback savepoint and keep going
1119                 cr.execute('ROLLBACK TO SAVEPOINT model_load_save')
1120         if any(message['type'] == 'error' for message in messages):
1121             cr.execute('ROLLBACK TO SAVEPOINT model_load')
1122             ids = False
1123         return {'ids': ids, 'messages': messages}
1124
1125     def _extract_records(self, cr, uid, fields_, data,
1126                          context=None, log=lambda a: None):
1127         """ Generates record dicts from the data sequence.
1128
1129         The result is a generator of dicts mapping field names to raw
1130         (unconverted, unvalidated) values.
1131
1132         For relational fields, if sub-fields were provided the value will be
1133         a list of sub-records
1134
1135         The following sub-fields may be set on the record (by key):
1136         * None is the name_get for the record (to use with name_create/name_search)
1137         * "id" is the External ID for the record
1138         * ".id" is the Database ID for the record
1139         """
1140         columns = dict((k, v.column) for k, v in self._all_columns.iteritems())
1141         # Fake columns to avoid special cases in extractor
1142         columns[None] = fields.char('rec_name')
1143         columns['id'] = fields.char('External ID')
1144         columns['.id'] = fields.integer('Database ID')
1145
1146         # m2o fields can't be on multiple lines so exclude them from the
1147         # is_relational field rows filter, but special-case it later on to
1148         # be handled with relational fields (as it can have subfields)
1149         is_relational = lambda field: columns[field]._type in ('one2many', 'many2many', 'many2one')
1150         get_o2m_values = itemgetter_tuple(
1151             [index for index, field in enumerate(fields_)
1152                   if columns[field[0]]._type == 'one2many'])
1153         get_nono2m_values = itemgetter_tuple(
1154             [index for index, field in enumerate(fields_)
1155                   if columns[field[0]]._type != 'one2many'])
1156         # Checks if the provided row has any non-empty non-relational field
1157         def only_o2m_values(row, f=get_nono2m_values, g=get_o2m_values):
1158             return any(g(row)) and not any(f(row))
1159
1160         index = 0
1161         while True:
1162             if index >= len(data): return
1163
1164             row = data[index]
1165             # copy non-relational fields to record dict
1166             record = dict((field[0], value)
1167                 for field, value in itertools.izip(fields_, row)
1168                 if not is_relational(field[0]))
1169
1170             # Get all following rows which have relational values attached to
1171             # the current record (no non-relational values)
1172             record_span = itertools.takewhile(
1173                 only_o2m_values, itertools.islice(data, index + 1, None))
1174             # stitch record row back on for relational fields
1175             record_span = list(itertools.chain([row], record_span))
1176             for relfield in set(
1177                     field[0] for field in fields_
1178                              if is_relational(field[0])):
1179                 column = columns[relfield]
1180                 # FIXME: how to not use _obj without relying on fields_get?
1181                 Model = self.pool[column._obj]
1182
1183                 # get only cells for this sub-field, should be strictly
1184                 # non-empty, field path [None] is for name_get column
1185                 indices, subfields = zip(*((index, field[1:] or [None])
1186                                            for index, field in enumerate(fields_)
1187                                            if field[0] == relfield))
1188
1189                 # return all rows which have at least one value for the
1190                 # subfields of relfield
1191                 relfield_data = filter(any, map(itemgetter_tuple(indices), record_span))
1192                 record[relfield] = [subrecord
1193                     for subrecord, _subinfo in Model._extract_records(
1194                         cr, uid, subfields, relfield_data,
1195                         context=context, log=log)]
1196
1197             yield record, {'rows': {
1198                 'from': index,
1199                 'to': index + len(record_span) - 1
1200             }}
1201             index += len(record_span)
1202     
1203     def _convert_records(self, cr, uid, records,
1204                          context=None, log=lambda a: None):
1205         """ Converts records from the source iterable (recursive dicts of
1206         strings) into forms which can be written to the database (via
1207         self.create or (ir.model.data)._update)
1208
1209         :returns: a list of triplets of (id, xid, record)
1210         :rtype: list((int|None, str|None, dict))
1211         """
1212         if context is None: context = {}
1213         Converter = self.pool['ir.fields.converter']
1214         columns = dict((k, v.column) for k, v in self._all_columns.iteritems())
1215         Translation = self.pool['ir.translation']
1216         field_names = dict(
1217             (f, (Translation._get_source(cr, uid, self._name + ',' + f, 'field',
1218                                          context.get('lang'))
1219                  or column.string))
1220             for f, column in columns.iteritems())
1221
1222         convert = Converter.for_model(cr, uid, self, context=context)
1223
1224         def _log(base, field, exception):
1225             type = 'warning' if isinstance(exception, Warning) else 'error'
1226             # logs the logical (not human-readable) field name for automated
1227             # processing of response, but injects human readable in message
1228             record = dict(base, type=type, field=field,
1229                           message=unicode(exception.args[0]) % base)
1230             if len(exception.args) > 1 and exception.args[1]:
1231                 record.update(exception.args[1])
1232             log(record)
1233
1234         stream = CountingStream(records)
1235         for record, extras in stream:
1236             dbid = False
1237             xid = False
1238             # name_get/name_create
1239             if None in record: pass
1240             # xid
1241             if 'id' in record:
1242                 xid = record['id']
1243             # dbid
1244             if '.id' in record:
1245                 try:
1246                     dbid = int(record['.id'])
1247                 except ValueError:
1248                     # in case of overridden id column
1249                     dbid = record['.id']
1250                 if not self.search(cr, uid, [('id', '=', dbid)], context=context):
1251                     log(dict(extras,
1252                         type='error',
1253                         record=stream.index,
1254                         field='.id',
1255                         message=_(u"Unknown database identifier '%s'") % dbid))
1256                     dbid = False
1257
1258             converted = convert(record, lambda field, err:\
1259                 _log(dict(extras, record=stream.index, field=field_names[field]), field, err))
1260
1261             yield dbid, xid, converted, dict(extras, record=stream.index)
1262
1263     @api.multi
1264     def _validate_fields(self, field_names):
1265         field_names = set(field_names)
1266
1267         # old-style constraint methods
1268         trans = self.env['ir.translation']
1269         cr, uid, context = self.env.args
1270         ids = self.ids
1271         errors = []
1272         for fun, msg, names in self._constraints:
1273             try:
1274                 # validation must be context-independent; call `fun` without context
1275                 valid = not (set(names) & field_names) or fun(self._model, cr, uid, ids)
1276                 extra_error = None
1277             except Exception, e:
1278                 _logger.debug('Exception while validating constraint', exc_info=True)
1279                 valid = False
1280                 extra_error = tools.ustr(e)
1281             if not valid:
1282                 if callable(msg):
1283                     res_msg = msg(self._model, cr, uid, ids, context=context)
1284                     if isinstance(res_msg, tuple):
1285                         template, params = res_msg
1286                         res_msg = template % params
1287                 else:
1288                     res_msg = trans._get_source(self._name, 'constraint', self.env.lang, msg)
1289                 if extra_error:
1290                     res_msg += "\n\n%s\n%s" % (_('Error details:'), extra_error)
1291                 errors.append(
1292                     _("Field(s) `%s` failed against a constraint: %s") %
1293                         (', '.join(names), res_msg)
1294                 )
1295         if errors:
1296             raise ValidationError('\n'.join(errors))
1297
1298         # new-style constraint methods
1299         for check in self._constraint_methods:
1300             if set(check._constrains) & field_names:
1301                 try:
1302                     check(self)
1303                 except ValidationError, e:
1304                     raise
1305                 except Exception, e:
1306                     raise ValidationError("Error while validating constraint\n\n%s" % tools.ustr(e))
1307
1308     @api.model
1309     def default_get(self, fields_list):
1310         """ default_get(fields) -> default_values
1311
1312         Return default values for the fields in `fields_list`. Default
1313         values are determined by the context, user defaults, and the model
1314         itself.
1315
1316         :param fields_list: a list of field names
1317         :return: a dictionary mapping each field name to its corresponding
1318             default value, if it has one.
1319
1320         """
1321         # trigger view init hook
1322         self.view_init(fields_list)
1323
1324         defaults = {}
1325         parent_fields = defaultdict(list)
1326
1327         for name in fields_list:
1328             # 1. look up context
1329             key = 'default_' + name
1330             if key in self._context:
1331                 defaults[name] = self._context[key]
1332                 continue
1333
1334             # 2. look up ir_values
1335             #    Note: performance is good, because get_defaults_dict is cached!
1336             ir_values_dict = self.env['ir.values'].get_defaults_dict(self._name)
1337             if name in ir_values_dict:
1338                 defaults[name] = ir_values_dict[name]
1339                 continue
1340
1341             field = self._fields.get(name)
1342
1343             # 3. look up property fields
1344             #    TODO: get rid of this one
1345             if field and field.company_dependent:
1346                 defaults[name] = self.env['ir.property'].get(name, self._name)
1347                 continue
1348
1349             # 4. look up field.default
1350             if field and field.default:
1351                 defaults[name] = field.default(self)
1352                 continue
1353
1354             # 5. delegate to parent model
1355             if field and field.inherited:
1356                 field = field.related_field
1357                 parent_fields[field.model_name].append(field.name)
1358
1359         # convert default values to the right format
1360         defaults = self._convert_to_cache(defaults, validate=False)
1361         defaults = self._convert_to_write(defaults)
1362
1363         # add default values for inherited fields
1364         for model, names in parent_fields.iteritems():
1365             defaults.update(self.env[model].default_get(names))
1366
1367         return defaults
1368
1369     def fields_get_keys(self, cr, user, context=None):
1370         res = self._columns.keys()
1371         # TODO I believe this loop can be replace by
1372         # res.extend(self._inherit_fields.key())
1373         for parent in self._inherits:
1374             res.extend(self.pool[parent].fields_get_keys(cr, user, context))
1375         return res
1376
1377     def _rec_name_fallback(self, cr, uid, context=None):
1378         rec_name = self._rec_name
1379         if rec_name not in self._columns:
1380             rec_name = self._columns.keys()[0] if len(self._columns.keys()) > 0 else "id"
1381         return rec_name
1382
1383     #
1384     # Overload this method if you need a window title which depends on the context
1385     #
1386     def view_header_get(self, cr, user, view_id=None, view_type='form', context=None):
1387         return False
1388
1389     def user_has_groups(self, cr, uid, groups, context=None):
1390         """Return true if the user is at least member of one of the groups
1391            in groups_str. Typically used to resolve `groups` attribute
1392            in view and model definitions.
1393
1394            :param str groups: comma-separated list of fully-qualified group
1395                               external IDs, e.g.: ``base.group_user,base.group_system``
1396            :return: True if the current user is a member of one of the
1397                     given groups
1398         """
1399         return any(self.pool['res.users'].has_group(cr, uid, group_ext_id)
1400                    for group_ext_id in groups.split(','))
1401
1402     def _get_default_form_view(self, cr, user, context=None):
1403         """ Generates a default single-line form view using all fields
1404         of the current model except the m2m and o2m ones.
1405
1406         :param cr: database cursor
1407         :param int user: user id
1408         :param dict context: connection context
1409         :returns: a form view as an lxml document
1410         :rtype: etree._Element
1411         """
1412         view = etree.Element('form', string=self._description)
1413         group = etree.SubElement(view, 'group', col="4")
1414         for fname, field in self._fields.iteritems():
1415             if field.automatic or field.type in ('one2many', 'many2many'):
1416                 continue
1417
1418             etree.SubElement(group, 'field', name=fname)
1419             if field.type == 'text':
1420                 etree.SubElement(group, 'newline')
1421         return view
1422
1423     def _get_default_search_view(self, cr, user, context=None):
1424         """ Generates a single-field search view, based on _rec_name.
1425
1426         :param cr: database cursor
1427         :param int user: user id
1428         :param dict context: connection context
1429         :returns: a tree view as an lxml document
1430         :rtype: etree._Element
1431         """
1432         view = etree.Element('search', string=self._description)
1433         etree.SubElement(view, 'field', name=self._rec_name_fallback(cr, user, context))
1434         return view
1435
1436     def _get_default_tree_view(self, cr, user, context=None):
1437         """ Generates a single-field tree view, based on _rec_name.
1438
1439         :param cr: database cursor
1440         :param int user: user id
1441         :param dict context: connection context
1442         :returns: a tree view as an lxml document
1443         :rtype: etree._Element
1444         """
1445         view = etree.Element('tree', string=self._description)
1446         etree.SubElement(view, 'field', name=self._rec_name_fallback(cr, user, context))
1447         return view
1448
1449     def _get_default_calendar_view(self, cr, user, context=None):
1450         """ Generates a default calendar view by trying to infer
1451         calendar fields from a number of pre-set attribute names
1452
1453         :param cr: database cursor
1454         :param int user: user id
1455         :param dict context: connection context
1456         :returns: a calendar view
1457         :rtype: etree._Element
1458         """
1459         def set_first_of(seq, in_, to):
1460             """Sets the first value of `seq` also found in `in_` to
1461             the `to` attribute of the view being closed over.
1462
1463             Returns whether it's found a suitable value (and set it on
1464             the attribute) or not
1465             """
1466             for item in seq:
1467                 if item in in_:
1468                     view.set(to, item)
1469                     return True
1470             return False
1471
1472         view = etree.Element('calendar', string=self._description)
1473         etree.SubElement(view, 'field', name=self._rec_name_fallback(cr, user, context))
1474
1475         if self._date_name not in self._columns:
1476             date_found = False
1477             for dt in ['date', 'date_start', 'x_date', 'x_date_start']:
1478                 if dt in self._columns:
1479                     self._date_name = dt
1480                     date_found = True
1481                     break
1482
1483             if not date_found:
1484                 raise except_orm(_('Invalid Object Architecture!'), _("Insufficient fields for Calendar View!"))
1485         view.set('date_start', self._date_name)
1486
1487         set_first_of(["user_id", "partner_id", "x_user_id", "x_partner_id"],
1488                      self._columns, 'color')
1489
1490         if not set_first_of(["date_stop", "date_end", "x_date_stop", "x_date_end"],
1491                             self._columns, 'date_stop'):
1492             if not set_first_of(["date_delay", "planned_hours", "x_date_delay", "x_planned_hours"],
1493                                 self._columns, 'date_delay'):
1494                 raise except_orm(
1495                     _('Invalid Object Architecture!'),
1496                     _("Insufficient fields to generate a Calendar View for %s, missing a date_stop or a date_delay" % self._name))
1497
1498         return view
1499
1500     def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
1501         """ fields_view_get([view_id | view_type='form'])
1502
1503         Get the detailed composition of the requested view like fields, model, view architecture
1504
1505         :param view_id: id of the view or None
1506         :param view_type: type of the view to return if view_id is None ('form', tree', ...)
1507         :param toolbar: true to include contextual actions
1508         :param submenu: deprecated
1509         :return: dictionary describing the composition of the requested view (including inherited views and extensions)
1510         :raise AttributeError:
1511                             * if the inherited view has unknown position to work with other than 'before', 'after', 'inside', 'replace'
1512                             * if some tag other than 'position' is found in parent view
1513         :raise Invalid ArchitectureError: if there is view type other than form, tree, calendar, search etc defined on the structure
1514         """
1515         if context is None:
1516             context = {}
1517         View = self.pool['ir.ui.view']
1518
1519         result = {
1520             'model': self._name,
1521             'field_parent': False,
1522         }
1523
1524         # try to find a view_id if none provided
1525         if not view_id:
1526             # <view_type>_view_ref in context can be used to overrride the default view
1527             view_ref_key = view_type + '_view_ref'
1528             view_ref = context.get(view_ref_key)
1529             if view_ref:
1530                 if '.' in view_ref:
1531                     module, view_ref = view_ref.split('.', 1)
1532                     cr.execute("SELECT res_id FROM ir_model_data WHERE model='ir.ui.view' AND module=%s AND name=%s", (module, view_ref))
1533                     view_ref_res = cr.fetchone()
1534                     if view_ref_res:
1535                         view_id = view_ref_res[0]
1536                 else:
1537                     _logger.warning('%r requires a fully-qualified external id (got: %r for model %s). '
1538                         'Please use the complete `module.view_id` form instead.', view_ref_key, view_ref,
1539                         self._name)
1540
1541             if not view_id:
1542                 # otherwise try to find the lowest priority matching ir.ui.view
1543                 view_id = View.default_view(cr, uid, self._name, view_type, context=context)
1544
1545         # context for post-processing might be overriden
1546         ctx = context
1547         if view_id:
1548             # read the view with inherited views applied
1549             root_view = View.read_combined(cr, uid, view_id, fields=['id', 'name', 'field_parent', 'type', 'model', 'arch'], context=context)
1550             result['arch'] = root_view['arch']
1551             result['name'] = root_view['name']
1552             result['type'] = root_view['type']
1553             result['view_id'] = root_view['id']
1554             result['field_parent'] = root_view['field_parent']
1555             # override context fro postprocessing
1556             if root_view.get('model') != self._name:
1557                 ctx = dict(context, base_model_name=root_view.get('model'))
1558         else:
1559             # fallback on default views methods if no ir.ui.view could be found
1560             try:
1561                 get_func = getattr(self, '_get_default_%s_view' % view_type)
1562                 arch_etree = get_func(cr, uid, context)
1563                 result['arch'] = etree.tostring(arch_etree, encoding='utf-8')
1564                 result['type'] = view_type
1565                 result['name'] = 'default'
1566             except AttributeError:
1567                 raise except_orm(_('Invalid Architecture!'), _("No default view of type '%s' could be found !") % view_type)
1568
1569         # Apply post processing, groups and modifiers etc...
1570         xarch, xfields = View.postprocess_and_fields(cr, uid, self._name, etree.fromstring(result['arch']), view_id, context=ctx)
1571         result['arch'] = xarch
1572         result['fields'] = xfields
1573
1574         # Add related action information if aksed
1575         if toolbar:
1576             toclean = ('report_sxw_content', 'report_rml_content', 'report_sxw', 'report_rml', 'report_sxw_content_data', 'report_rml_content_data')
1577             def clean(x):
1578                 x = x[2]
1579                 for key in toclean:
1580                     x.pop(key, None)
1581                 return x
1582             ir_values_obj = self.pool.get('ir.values')
1583             resprint = ir_values_obj.get(cr, uid, 'action', 'client_print_multi', [(self._name, False)], False, context)
1584             resaction = ir_values_obj.get(cr, uid, 'action', 'client_action_multi', [(self._name, False)], False, context)
1585             resrelate = ir_values_obj.get(cr, uid, 'action', 'client_action_relate', [(self._name, False)], False, context)
1586             resaction = [clean(action) for action in resaction if view_type == 'tree' or not action[2].get('multi')]
1587             resprint = [clean(print_) for print_ in resprint if view_type == 'tree' or not print_[2].get('multi')]
1588             #When multi="True" set it will display only in More of the list view
1589             resrelate = [clean(action) for action in resrelate
1590                          if (action[2].get('multi') and view_type == 'tree') or (not action[2].get('multi') and view_type == 'form')]
1591
1592             for x in itertools.chain(resprint, resaction, resrelate):
1593                 x['string'] = x['name']
1594
1595             result['toolbar'] = {
1596                 'print': resprint,
1597                 'action': resaction,
1598                 'relate': resrelate
1599             }
1600         return result
1601
1602     def get_formview_id(self, cr, uid, id, context=None):
1603         """ Return an view id to open the document with. This method is meant to be
1604             overridden in addons that want to give specific view ids for example.
1605
1606             :param int id: id of the document to open
1607         """
1608         return False
1609
1610     def get_formview_action(self, cr, uid, id, context=None):
1611         """ Return an action to open the document. This method is meant to be
1612             overridden in addons that want to give specific view ids for example.
1613
1614             :param int id: id of the document to open
1615         """
1616         view_id = self.get_formview_id(cr, uid, id, context=context)
1617         return {
1618             'type': 'ir.actions.act_window',
1619             'res_model': self._name,
1620             'view_type': 'form',
1621             'view_mode': 'form',
1622             'views': [(view_id, 'form')],
1623             'target': 'current',
1624             'res_id': id,
1625         }
1626
1627     def get_access_action(self, cr, uid, id, context=None):
1628         """ Return an action to open the document. This method is meant to be
1629         overridden in addons that want to give specific access to the document.
1630         By default it opens the formview of the document.
1631
1632         :paramt int id: id of the document to open
1633         """
1634         return self.get_formview_action(cr, uid, id, context=context)
1635
1636     def _view_look_dom_arch(self, cr, uid, node, view_id, context=None):
1637         return self.pool['ir.ui.view'].postprocess_and_fields(
1638             cr, uid, self._name, node, view_id, context=context)
1639
1640     def search_count(self, cr, user, args, context=None):
1641         """ search_count(args) -> int
1642
1643         Returns the number of records in the current model matching :ref:`the
1644         provided domain <reference/orm/domains>`.
1645         """
1646         res = self.search(cr, user, args, context=context, count=True)
1647         if isinstance(res, list):
1648             return len(res)
1649         return res
1650
1651     @api.returns('self')
1652     def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False):
1653         """ search(args[, offset=0][, limit=None][, order=None][, count=False])
1654
1655         Searches for records based on the ``args``
1656         :ref:`search domain <reference/orm/domains>`.
1657
1658         :param args: :ref:`A search domain <reference/orm/domains>`. Use an empty
1659                      list to match all records.
1660         :param int offset: number of results to ignore (default: none)
1661         :param int limit: maximum number of records to return (default: all)
1662         :param str order: sort string
1663         :param bool count: if ``True``, the call should return the number of
1664                            records matching ``args`` rather than the records
1665                            themselves.
1666         :returns: at most ``limit`` records matching the search criteria
1667
1668         :raise AccessError: * if user tries to bypass access rules for read on the requested object.
1669         """
1670         return self._search(cr, user, args, offset=offset, limit=limit, order=order, context=context, count=count)
1671
1672     #
1673     # display_name, name_get, name_create, name_search
1674     #
1675
1676     @api.depends(lambda self: (self._rec_name,) if self._rec_name else ())
1677     def _compute_display_name(self):
1678         names = dict(self.name_get())
1679         for record in self:
1680             record.display_name = names.get(record.id, False)
1681
1682     @api.multi
1683     def name_get(self):
1684         """ name_get() -> [(id, name), ...]
1685
1686         Returns a textual representation for the records in ``self``.
1687         By default this is the value of the ``display_name`` field.
1688
1689         :return: list of pairs ``(id, text_repr)`` for each records
1690         :rtype: list(tuple)
1691         """
1692         result = []
1693         name = self._rec_name
1694         if name in self._fields:
1695             convert = self._fields[name].convert_to_display_name
1696             for record in self:
1697                 result.append((record.id, convert(record[name])))
1698         else:
1699             for record in self:
1700                 result.append((record.id, "%s,%s" % (record._name, record.id)))
1701
1702         return result
1703
1704     @api.model
1705     def name_create(self, name):
1706         """ name_create(name) -> record
1707
1708         Create a new record by calling :meth:`~.create` with only one value
1709         provided: the display name of the new record.
1710
1711         The new record will be initialized with any default values
1712         applicable to this model, or provided through the context. The usual
1713         behavior of :meth:`~.create` applies.
1714
1715         :param name: display name of the record to create
1716         :rtype: tuple
1717         :return: the :meth:`~.name_get` pair value of the created record
1718         """
1719         if self._rec_name:
1720             record = self.create({self._rec_name: name})
1721             return record.name_get()[0]
1722         else:
1723             _logger.warning("Cannot execute name_create, no _rec_name defined on %s", self._name)
1724             return False
1725
1726     @api.model
1727     def name_search(self, name='', args=None, operator='ilike', limit=100):
1728         """ name_search(name='', args=None, operator='ilike', limit=100) -> records
1729
1730         Search for records that have a display name matching the given
1731         `name` pattern when compared with the given `operator`, while also
1732         matching the optional search domain (`args`).
1733
1734         This is used for example to provide suggestions based on a partial
1735         value for a relational field. Sometimes be seen as the inverse
1736         function of :meth:`~.name_get`, but it is not guaranteed to be.
1737
1738         This method is equivalent to calling :meth:`~.search` with a search
1739         domain based on ``display_name`` and then :meth:`~.name_get` on the
1740         result of the search.
1741
1742         :param str name: the name pattern to match
1743         :param list args: optional search domain (see :meth:`~.search` for
1744                           syntax), specifying further restrictions
1745         :param str operator: domain operator for matching `name`, such as
1746                              ``'like'`` or ``'='``.
1747         :param int limit: optional max number of records to return
1748         :rtype: list
1749         :return: list of pairs ``(id, text_repr)`` for all matching records.
1750         """
1751         return self._name_search(name, args, operator, limit=limit)
1752
1753     def _name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100, name_get_uid=None):
1754         # private implementation of name_search, allows passing a dedicated user
1755         # for the name_get part to solve some access rights issues
1756         args = list(args or [])
1757         # optimize out the default criterion of ``ilike ''`` that matches everything
1758         if not self._rec_name:
1759             _logger.warning("Cannot execute name_search, no _rec_name defined on %s", self._name)
1760         elif not (name == '' and operator == 'ilike'):
1761             args += [(self._rec_name, operator, name)]
1762         access_rights_uid = name_get_uid or user
1763         ids = self._search(cr, user, args, limit=limit, context=context, access_rights_uid=access_rights_uid)
1764         res = self.name_get(cr, access_rights_uid, ids, context)
1765         return res
1766
1767     def read_string(self, cr, uid, id, langs, fields=None, context=None):
1768         res = {}
1769         res2 = {}
1770         self.pool.get('ir.translation').check_access_rights(cr, uid, 'read')
1771         if not fields:
1772             fields = self._columns.keys() + self._inherit_fields.keys()
1773         #FIXME: collect all calls to _get_source into one SQL call.
1774         for lang in langs:
1775             res[lang] = {'code': lang}
1776             for f in fields:
1777                 if f in self._columns:
1778                     res_trans = self.pool.get('ir.translation')._get_source(cr, uid, self._name+','+f, 'field', lang)
1779                     if res_trans:
1780                         res[lang][f] = res_trans
1781                     else:
1782                         res[lang][f] = self._columns[f].string
1783         for table in self._inherits:
1784             cols = intersect(self._inherit_fields.keys(), fields)
1785             res2 = self.pool[table].read_string(cr, uid, id, langs, cols, context)
1786         for lang in res2:
1787             if lang in res:
1788                 res[lang]['code'] = lang
1789             for f in res2[lang]:
1790                 res[lang][f] = res2[lang][f]
1791         return res
1792
1793     def write_string(self, cr, uid, id, langs, vals, context=None):
1794         self.pool.get('ir.translation').check_access_rights(cr, uid, 'write')
1795         #FIXME: try to only call the translation in one SQL
1796         for lang in langs:
1797             for field in vals:
1798                 if field in self._columns:
1799                     src = self._columns[field].string
1800                     self.pool.get('ir.translation')._set_ids(cr, uid, self._name+','+field, 'field', lang, [0], vals[field], src)
1801         for table in self._inherits:
1802             cols = intersect(self._inherit_fields.keys(), vals)
1803             if cols:
1804                 self.pool[table].write_string(cr, uid, id, langs, vals, context)
1805         return True
1806
1807     def _add_missing_default_values(self, cr, uid, values, context=None):
1808         # avoid overriding inherited values when parent is set
1809         avoid_tables = []
1810         for tables, parent_field in self._inherits.items():
1811             if parent_field in values:
1812                 avoid_tables.append(tables)
1813
1814         # compute missing fields
1815         missing_defaults = set()
1816         for field in self._columns.keys():
1817             if not field in values:
1818                 missing_defaults.add(field)
1819         for field in self._inherit_fields.keys():
1820             if (field not in values) and (self._inherit_fields[field][0] not in avoid_tables):
1821                 missing_defaults.add(field)
1822         # discard magic fields
1823         missing_defaults -= set(MAGIC_COLUMNS)
1824
1825         if missing_defaults:
1826             # override defaults with the provided values, never allow the other way around
1827             defaults = self.default_get(cr, uid, list(missing_defaults), context)
1828             for dv in defaults:
1829                 if ((dv in self._columns and self._columns[dv]._type == 'many2many') \
1830                      or (dv in self._inherit_fields and self._inherit_fields[dv][2]._type == 'many2many')) \
1831                         and defaults[dv] and isinstance(defaults[dv][0], (int, long)):
1832                     defaults[dv] = [(6, 0, defaults[dv])]
1833                 if (dv in self._columns and self._columns[dv]._type == 'one2many' \
1834                     or (dv in self._inherit_fields and self._inherit_fields[dv][2]._type == 'one2many')) \
1835                         and isinstance(defaults[dv], (list, tuple)) and defaults[dv] and isinstance(defaults[dv][0], dict):
1836                     defaults[dv] = [(0, 0, x) for x in defaults[dv]]
1837             defaults.update(values)
1838             values = defaults
1839         return values
1840
1841     def clear_caches(self):
1842         """ Clear the caches
1843
1844         This clears the caches associated to methods decorated with
1845         ``tools.ormcache`` or ``tools.ormcache_multi``.
1846         """
1847         try:
1848             self._ormcache.clear()
1849             self.pool._any_cache_cleared = True
1850         except AttributeError:
1851             pass
1852
1853
1854     def _read_group_fill_results(self, cr, uid, domain, groupby, remaining_groupbys,
1855                                  aggregated_fields, count_field,
1856                                  read_group_result, read_group_order=None, context=None):
1857         """Helper method for filling in empty groups for all possible values of
1858            the field being grouped by"""
1859
1860         # self._group_by_full should map groupable fields to a method that returns
1861         # a list of all aggregated values that we want to display for this field,
1862         # in the form of a m2o-like pair (key,label).
1863         # This is useful to implement kanban views for instance, where all columns
1864         # should be displayed even if they don't contain any record.
1865
1866         # Grab the list of all groups that should be displayed, including all present groups
1867         present_group_ids = [x[groupby][0] for x in read_group_result if x[groupby]]
1868         all_groups,folded = self._group_by_full[groupby](self, cr, uid, present_group_ids, domain,
1869                                                   read_group_order=read_group_order,
1870                                                   access_rights_uid=openerp.SUPERUSER_ID,
1871                                                   context=context)
1872
1873         result_template = dict.fromkeys(aggregated_fields, False)
1874         result_template[groupby + '_count'] = 0
1875         if remaining_groupbys:
1876             result_template['__context'] = {'group_by': remaining_groupbys}
1877
1878         # Merge the left_side (current results as dicts) with the right_side (all
1879         # possible values as m2o pairs). Both lists are supposed to be using the
1880         # same ordering, and can be merged in one pass.
1881         result = []
1882         known_values = {}
1883         def append_left(left_side):
1884             grouped_value = left_side[groupby] and left_side[groupby][0]
1885             if not grouped_value in known_values:
1886                 result.append(left_side)
1887                 known_values[grouped_value] = left_side
1888             else:
1889                 known_values[grouped_value].update({count_field: left_side[count_field]})
1890         def append_right(right_side):
1891             grouped_value = right_side[0]
1892             if not grouped_value in known_values:
1893                 line = dict(result_template)
1894                 line[groupby] = right_side
1895                 line['__domain'] = [(groupby,'=',grouped_value)] + domain
1896                 result.append(line)
1897                 known_values[grouped_value] = line
1898         while read_group_result or all_groups:
1899             left_side = read_group_result[0] if read_group_result else None
1900             right_side = all_groups[0] if all_groups else None
1901             assert left_side is None or left_side[groupby] is False \
1902                  or isinstance(left_side[groupby], (tuple,list)), \
1903                 'M2O-like pair expected, got %r' % left_side[groupby]
1904             assert right_side is None or isinstance(right_side, (tuple,list)), \
1905                 'M2O-like pair expected, got %r' % right_side
1906             if left_side is None:
1907                 append_right(all_groups.pop(0))
1908             elif right_side is None:
1909                 append_left(read_group_result.pop(0))
1910             elif left_side[groupby] == right_side:
1911                 append_left(read_group_result.pop(0))
1912                 all_groups.pop(0) # discard right_side
1913             elif not left_side[groupby] or not left_side[groupby][0]:
1914                 # left side == "Undefined" entry, not present on right_side
1915                 append_left(read_group_result.pop(0))
1916             else:
1917                 append_right(all_groups.pop(0))
1918
1919         if folded:
1920             for r in result:
1921                 r['__fold'] = folded.get(r[groupby] and r[groupby][0], False)
1922         return result
1923
1924     def _read_group_prepare(self, orderby, aggregated_fields, annotated_groupbys, query):
1925         """
1926         Prepares the GROUP BY and ORDER BY terms for the read_group method. Adds the missing JOIN clause
1927         to the query if order should be computed against m2o field. 
1928         :param orderby: the orderby definition in the form "%(field)s %(order)s"
1929         :param aggregated_fields: list of aggregated fields in the query
1930         :param annotated_groupbys: list of dictionaries returned by _read_group_process_groupby
1931                 These dictionaries contains the qualified name of each groupby
1932                 (fully qualified SQL name for the corresponding field),
1933                 and the (non raw) field name.
1934         :param osv.Query query: the query under construction
1935         :return: (groupby_terms, orderby_terms)
1936         """
1937         orderby_terms = []
1938         groupby_terms = [gb['qualified_field'] for gb in annotated_groupbys]
1939         groupby_fields = [gb['groupby'] for gb in annotated_groupbys]
1940         if not orderby:
1941             return groupby_terms, orderby_terms
1942
1943         self._check_qorder(orderby)
1944         for order_part in orderby.split(','):
1945             order_split = order_part.split()
1946             order_field = order_split[0]
1947             if order_field in groupby_fields:
1948
1949                 if self._all_columns[order_field.split(':')[0]].column._type == 'many2one':
1950                     order_clause = self._generate_order_by(order_part, query).replace('ORDER BY ', '')
1951                     if order_clause:
1952                         orderby_terms.append(order_clause)
1953                         groupby_terms += [order_term.split()[0] for order_term in order_clause.split(',')]
1954                 else:
1955                     order = '"%s" %s' % (order_field, '' if len(order_split) == 1 else order_split[1])
1956                     orderby_terms.append(order)
1957             elif order_field in aggregated_fields:
1958                 orderby_terms.append(order_part)
1959             else:
1960                 # Cannot order by a field that will not appear in the results (needs to be grouped or aggregated)
1961                 _logger.warn('%s: read_group order by `%s` ignored, cannot sort on empty columns (not grouped/aggregated)',
1962                              self._name, order_part)
1963         return groupby_terms, orderby_terms
1964
1965     def _read_group_process_groupby(self, gb, query, context):
1966         """
1967             Helper method to collect important information about groupbys: raw
1968             field name, type, time informations, qualified name, ...
1969         """
1970         split = gb.split(':')
1971         field_type = self._all_columns[split[0]].column._type
1972         gb_function = split[1] if len(split) == 2 else None
1973         temporal = field_type in ('date', 'datetime')
1974         tz_convert = field_type == 'datetime' and context.get('tz') in pytz.all_timezones
1975         qualified_field = self._inherits_join_calc(split[0], query)
1976         if temporal:
1977             display_formats = {
1978                 'day': 'dd MMM YYYY', 
1979                 'week': "'W'w YYYY", 
1980                 'month': 'MMMM YYYY', 
1981                 'quarter': 'QQQ YYYY', 
1982                 'year': 'YYYY'
1983             }
1984             time_intervals = {
1985                 'day': dateutil.relativedelta.relativedelta(days=1),
1986                 'week': datetime.timedelta(days=7),
1987                 'month': dateutil.relativedelta.relativedelta(months=1),
1988                 'quarter': dateutil.relativedelta.relativedelta(months=3),
1989                 'year': dateutil.relativedelta.relativedelta(years=1)
1990             }
1991             if tz_convert:
1992                 qualified_field = "timezone('%s', timezone('UTC',%s))" % (context.get('tz', 'UTC'), qualified_field)
1993             qualified_field = "date_trunc('%s', %s)" % (gb_function or 'month', qualified_field)
1994         if field_type == 'boolean':
1995             qualified_field = "coalesce(%s,false)" % qualified_field
1996         return {
1997             'field': split[0],
1998             'groupby': gb,
1999             'type': field_type, 
2000             'display_format': display_formats[gb_function or 'month'] if temporal else None,
2001             'interval': time_intervals[gb_function or 'month'] if temporal else None,                
2002             'tz_convert': tz_convert,
2003             'qualified_field': qualified_field
2004         }
2005
2006     def _read_group_prepare_data(self, key, value, groupby_dict, context):
2007         """
2008             Helper method to sanitize the data received by read_group. The None
2009             values are converted to False, and the date/datetime are formatted,
2010             and corrected according to the timezones.
2011         """
2012         value = False if value is None else value
2013         gb = groupby_dict.get(key)
2014         if gb and gb['type'] in ('date', 'datetime') and value:
2015             if isinstance(value, basestring):
2016                 dt_format = DEFAULT_SERVER_DATETIME_FORMAT if gb['type'] == 'datetime' else DEFAULT_SERVER_DATE_FORMAT
2017                 value = datetime.datetime.strptime(value, dt_format)
2018             if gb['tz_convert']:
2019                 value =  pytz.timezone(context['tz']).localize(value)
2020         return value
2021
2022     def _read_group_get_domain(self, groupby, value):
2023         """
2024             Helper method to construct the domain corresponding to a groupby and 
2025             a given value. This is mostly relevant for date/datetime.
2026         """
2027         if groupby['type'] in ('date', 'datetime') and value:
2028             dt_format = DEFAULT_SERVER_DATETIME_FORMAT if groupby['type'] == 'datetime' else DEFAULT_SERVER_DATE_FORMAT
2029             domain_dt_begin = value
2030             domain_dt_end = value + groupby['interval']
2031             if groupby['tz_convert']:
2032                 domain_dt_begin = domain_dt_begin.astimezone(pytz.utc)
2033                 domain_dt_end = domain_dt_end.astimezone(pytz.utc)
2034             return [(groupby['field'], '>=', domain_dt_begin.strftime(dt_format)),
2035                    (groupby['field'], '<', domain_dt_end.strftime(dt_format))]
2036         if groupby['type'] == 'many2one' and value:
2037                 value = value[0]
2038         return [(groupby['field'], '=', value)]
2039
2040     def _read_group_format_result(self, data, annotated_groupbys, groupby, groupby_dict, domain, context):
2041         """
2042             Helper method to format the data contained in the dictianary data by 
2043             adding the domain corresponding to its values, the groupbys in the 
2044             context and by properly formatting the date/datetime values. 
2045         """
2046         domain_group = [dom for gb in annotated_groupbys for dom in self._read_group_get_domain(gb, data[gb['groupby']])]
2047         for k,v in data.iteritems():
2048             gb = groupby_dict.get(k)
2049             if gb and gb['type'] in ('date', 'datetime') and v:
2050                 data[k] = babel.dates.format_date(v, format=gb['display_format'], locale=context.get('lang', 'en_US'))
2051
2052         data['__domain'] = domain_group + domain 
2053         if len(groupby) - len(annotated_groupbys) >= 1:
2054             data['__context'] = { 'group_by': groupby[len(annotated_groupbys):]}
2055         del data['id']
2056         return data
2057
2058     def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False, lazy=True):
2059         """
2060         Get the list of records in list view grouped by the given ``groupby`` fields
2061
2062         :param cr: database cursor
2063         :param uid: current user id
2064         :param domain: list specifying search criteria [['field_name', 'operator', 'value'], ...]
2065         :param list fields: list of fields present in the list view specified on the object
2066         :param list groupby: list of groupby descriptions by which the records will be grouped.  
2067                 A groupby description is either a field (then it will be grouped by that field)
2068                 or a string 'field:groupby_function'.  Right now, the only functions supported
2069                 are 'day', 'week', 'month', 'quarter' or 'year', and they only make sense for 
2070                 date/datetime fields.
2071         :param int offset: optional number of records to skip
2072         :param int limit: optional max number of records to return
2073         :param dict context: context arguments, like lang, time zone. 
2074         :param list orderby: optional ``order by`` specification, for
2075                              overriding the natural sort ordering of the
2076                              groups, see also :py:meth:`~osv.osv.osv.search`
2077                              (supported only for many2one fields currently)
2078         :param bool lazy: if true, the results are only grouped by the first groupby and the 
2079                 remaining groupbys are put in the __context key.  If false, all the groupbys are
2080                 done in one call.
2081         :return: list of dictionaries(one dictionary for each record) containing:
2082
2083                     * the values of fields grouped by the fields in ``groupby`` argument
2084                     * __domain: list of tuples specifying the search criteria
2085                     * __context: dictionary with argument like ``groupby``
2086         :rtype: [{'field_name_1': value, ...]
2087         :raise AccessError: * if user has no read rights on the requested object
2088                             * if user tries to bypass access rules for read on the requested object
2089         """
2090         if context is None:
2091             context = {}
2092         self.check_access_rights(cr, uid, 'read')
2093         query = self._where_calc(cr, uid, domain, context=context) 
2094         fields = fields or self._columns.keys()
2095
2096         groupby = [groupby] if isinstance(groupby, basestring) else groupby
2097         groupby_list = groupby[:1] if lazy else groupby
2098         annotated_groupbys = [self._read_group_process_groupby(gb, query, context) 
2099                                     for gb in groupby_list]
2100         groupby_fields = [g['field'] for g in annotated_groupbys]
2101         order = orderby or ','.join([g for g in groupby_list])
2102         groupby_dict = {gb['groupby']: gb for gb in annotated_groupbys}
2103
2104         self._apply_ir_rules(cr, uid, query, 'read', context=context)
2105         for gb in groupby_fields:
2106             assert gb in fields, "Fields in 'groupby' must appear in the list of fields to read (perhaps it's missing in the list view?)"
2107             groupby_def = self._columns.get(gb) or (self._inherit_fields.get(gb) and self._inherit_fields.get(gb)[2])
2108             assert groupby_def and groupby_def._classic_write, "Fields in 'groupby' must be regular database-persisted fields (no function or related fields), or function fields with store=True"
2109             if not (gb in self._all_columns):
2110                 # Don't allow arbitrary values, as this would be a SQL injection vector!
2111                 raise except_orm(_('Invalid group_by'),
2112                                  _('Invalid group_by specification: "%s".\nA group_by specification must be a list of valid fields.')%(gb,))
2113
2114         aggregated_fields = [
2115             f for f in fields
2116             if f not in ('id', 'sequence')
2117             if f not in groupby_fields
2118             if f in self._all_columns
2119             if self._all_columns[f].column._type in ('integer', 'float')
2120             if getattr(self._all_columns[f].column, '_classic_write')]
2121
2122         field_formatter = lambda f: (self._all_columns[f].column.group_operator or 'sum', self._inherits_join_calc(f, query), f)
2123         select_terms = ["%s(%s) AS %s" % field_formatter(f) for f in aggregated_fields]
2124
2125         for gb in annotated_groupbys:
2126             select_terms.append('%s as "%s" ' % (gb['qualified_field'], gb['groupby']))
2127
2128         groupby_terms, orderby_terms = self._read_group_prepare(order, aggregated_fields, annotated_groupbys, query)
2129         from_clause, where_clause, where_clause_params = query.get_sql()
2130         if lazy and (len(groupby_fields) >= 2 or not context.get('group_by_no_leaf')):
2131             count_field = groupby_fields[0] if len(groupby_fields) >= 1 else '_'
2132         else:
2133             count_field = '_'
2134         count_field += '_count'
2135
2136         prefix_terms = lambda prefix, terms: (prefix + " " + ",".join(terms)) if terms else ''
2137         prefix_term = lambda prefix, term: ('%s %s' % (prefix, term)) if term else ''
2138
2139         query = """
2140             SELECT min(%(table)s.id) AS id, count(%(table)s.id) AS %(count_field)s %(extra_fields)s
2141             FROM %(from)s
2142             %(where)s
2143             %(groupby)s
2144             %(orderby)s
2145             %(limit)s
2146             %(offset)s
2147         """ % {
2148             'table': self._table,
2149             'count_field': count_field,
2150             'extra_fields': prefix_terms(',', select_terms),
2151             'from': from_clause,
2152             'where': prefix_term('WHERE', where_clause),
2153             'groupby': prefix_terms('GROUP BY', groupby_terms),
2154             'orderby': prefix_terms('ORDER BY', orderby_terms),
2155             'limit': prefix_term('LIMIT', int(limit) if limit else None),
2156             'offset': prefix_term('OFFSET', int(offset) if limit else None),
2157         }
2158         cr.execute(query, where_clause_params)
2159         fetched_data = cr.dictfetchall()
2160
2161         if not groupby_fields:
2162             return fetched_data
2163
2164         many2onefields = [gb['field'] for gb in annotated_groupbys if gb['type'] == 'many2one']
2165         if many2onefields:
2166             data_ids = [r['id'] for r in fetched_data]
2167             many2onefields = list(set(many2onefields))
2168             data_dict = {d['id']: d for d in self.read(cr, uid, data_ids, many2onefields, context=context)} 
2169             for d in fetched_data:
2170                 d.update(data_dict[d['id']])
2171
2172         data = map(lambda r: {k: self._read_group_prepare_data(k,v, groupby_dict, context) for k,v in r.iteritems()}, fetched_data)
2173         result = [self._read_group_format_result(d, annotated_groupbys, groupby, groupby_dict, domain, context) for d in data]
2174         if lazy and groupby_fields[0] in self._group_by_full:
2175             # Right now, read_group only fill results in lazy mode (by default).
2176             # If you need to have the empty groups in 'eager' mode, then the
2177             # method _read_group_fill_results need to be completely reimplemented
2178             # in a sane way 
2179             result = self._read_group_fill_results(cr, uid, domain, groupby_fields[0], groupby[len(annotated_groupbys):],
2180                                                        aggregated_fields, count_field, result, read_group_order=order,
2181                                                        context=context)
2182         return result
2183
2184     def _inherits_join_add(self, current_model, parent_model_name, query):
2185         """
2186         Add missing table SELECT and JOIN clause to ``query`` for reaching the parent table (no duplicates)
2187         :param current_model: current model object
2188         :param parent_model_name: name of the parent model for which the clauses should be added
2189         :param query: query object on which the JOIN should be added
2190         """
2191         inherits_field = current_model._inherits[parent_model_name]
2192         parent_model = self.pool[parent_model_name]
2193         parent_alias, parent_alias_statement = query.add_join((current_model._table, parent_model._table, inherits_field, 'id', inherits_field), implicit=True)
2194         return parent_alias
2195
2196     def _inherits_join_calc(self, field, query):
2197         """
2198         Adds missing table select and join clause(s) to ``query`` for reaching
2199         the field coming from an '_inherits' parent table (no duplicates).
2200
2201         :param field: name of inherited field to reach
2202         :param query: query object on which the JOIN should be added
2203         :return: qualified name of field, to be used in SELECT clause
2204         """
2205         current_table = self
2206         parent_alias = '"%s"' % current_table._table
2207         while field in current_table._inherit_fields and not field in current_table._columns:
2208             parent_model_name = current_table._inherit_fields[field][0]
2209             parent_table = self.pool[parent_model_name]
2210             parent_alias = self._inherits_join_add(current_table, parent_model_name, query)
2211             current_table = parent_table
2212         return '%s."%s"' % (parent_alias, field)
2213
2214     def _parent_store_compute(self, cr):
2215         if not self._parent_store:
2216             return
2217         _logger.info('Computing parent left and right for table %s...', self._table)
2218         def browse_rec(root, pos=0):
2219             # TODO: set order
2220             where = self._parent_name+'='+str(root)
2221             if not root:
2222                 where = self._parent_name+' IS NULL'
2223             if self._parent_order:
2224                 where += ' order by '+self._parent_order
2225             cr.execute('SELECT id FROM '+self._table+' WHERE '+where)
2226             pos2 = pos + 1
2227             for id in cr.fetchall():
2228                 pos2 = browse_rec(id[0], pos2)
2229             cr.execute('update '+self._table+' set parent_left=%s, parent_right=%s where id=%s', (pos, pos2, root))
2230             return pos2 + 1
2231         query = 'SELECT id FROM '+self._table+' WHERE '+self._parent_name+' IS NULL'
2232         if self._parent_order:
2233             query += ' order by ' + self._parent_order
2234         pos = 0
2235         cr.execute(query)
2236         for (root,) in cr.fetchall():
2237             pos = browse_rec(root, pos)
2238         self.invalidate_cache(cr, SUPERUSER_ID, ['parent_left', 'parent_right'])
2239         return True
2240
2241     def _update_store(self, cr, f, k):
2242         _logger.info("storing computed values of fields.function '%s'", k)
2243         ss = self._columns[k]._symbol_set
2244         update_query = 'UPDATE "%s" SET "%s"=%s WHERE id=%%s' % (self._table, k, ss[0])
2245         cr.execute('select id from '+self._table)
2246         ids_lst = map(lambda x: x[0], cr.fetchall())
2247         while ids_lst:
2248             iids = ids_lst[:AUTOINIT_RECALCULATE_STORED_FIELDS]
2249             ids_lst = ids_lst[AUTOINIT_RECALCULATE_STORED_FIELDS:]
2250             res = f.get(cr, self, iids, k, SUPERUSER_ID, {})
2251             for key, val in res.items():
2252                 if f._multi:
2253                     val = val[k]
2254                 # if val is a many2one, just write the ID
2255                 if type(val) == tuple:
2256                     val = val[0]
2257                 if val is not False:
2258                     cr.execute(update_query, (ss[1](val), key))
2259
2260     @api.model
2261     def _check_selection_field_value(self, field, value):
2262         """ Check whether value is among the valid values for the given
2263             selection/reference field, and raise an exception if not.
2264         """
2265         field = self._fields[field]
2266         field.convert_to_cache(value, self)
2267
2268     def _check_removed_columns(self, cr, log=False):
2269         # iterate on the database columns to drop the NOT NULL constraints
2270         # of fields which were required but have been removed (or will be added by another module)
2271         columns = [c for c in self._columns if not (isinstance(self._columns[c], fields.function) and not self._columns[c].store)]
2272         columns += MAGIC_COLUMNS
2273         cr.execute("SELECT a.attname, a.attnotnull"
2274                    "  FROM pg_class c, pg_attribute a"
2275                    " WHERE c.relname=%s"
2276                    "   AND c.oid=a.attrelid"
2277                    "   AND a.attisdropped=%s"
2278                    "   AND pg_catalog.format_type(a.atttypid, a.atttypmod) NOT IN ('cid', 'tid', 'oid', 'xid')"
2279                    "   AND a.attname NOT IN %s", (self._table, False, tuple(columns))),
2280
2281         for column in cr.dictfetchall():
2282             if log:
2283                 _logger.debug("column %s is in the table %s but not in the corresponding object %s",
2284                               column['attname'], self._table, self._name)
2285             if column['attnotnull']:
2286                 cr.execute('ALTER TABLE "%s" ALTER COLUMN "%s" DROP NOT NULL' % (self._table, column['attname']))
2287                 _schema.debug("Table '%s': column '%s': dropped NOT NULL constraint",
2288                               self._table, column['attname'])
2289
2290     def _save_constraint(self, cr, constraint_name, type):
2291         """
2292         Record the creation of a constraint for this model, to make it possible
2293         to delete it later when the module is uninstalled. Type can be either
2294         'f' or 'u' depending on the constraint being a foreign key or not.
2295         """
2296         if not self._module:
2297             # no need to save constraints for custom models as they're not part
2298             # of any module
2299             return
2300         assert type in ('f', 'u')
2301         cr.execute("""
2302             SELECT 1 FROM ir_model_constraint, ir_module_module
2303             WHERE ir_model_constraint.module=ir_module_module.id
2304                 AND ir_model_constraint.name=%s
2305                 AND ir_module_module.name=%s
2306             """, (constraint_name, self._module))
2307         if not cr.rowcount:
2308             cr.execute("""
2309                 INSERT INTO ir_model_constraint
2310                     (name, date_init, date_update, module, model, type)
2311                 VALUES (%s, now() AT TIME ZONE 'UTC', now() AT TIME ZONE 'UTC',
2312                     (SELECT id FROM ir_module_module WHERE name=%s),
2313                     (SELECT id FROM ir_model WHERE model=%s), %s)""",
2314                     (constraint_name, self._module, self._name, type))
2315
2316     def _save_relation_table(self, cr, relation_table):
2317         """
2318         Record the creation of a many2many for this model, to make it possible
2319         to delete it later when the module is uninstalled.
2320         """
2321         cr.execute("""
2322             SELECT 1 FROM ir_model_relation, ir_module_module
2323             WHERE ir_model_relation.module=ir_module_module.id
2324                 AND ir_model_relation.name=%s
2325                 AND ir_module_module.name=%s
2326             """, (relation_table, self._module))
2327         if not cr.rowcount:
2328             cr.execute("""INSERT INTO ir_model_relation (name, date_init, date_update, module, model)
2329                                  VALUES (%s, now() AT TIME ZONE 'UTC', now() AT TIME ZONE 'UTC',
2330                     (SELECT id FROM ir_module_module WHERE name=%s),
2331                     (SELECT id FROM ir_model WHERE model=%s))""",
2332                        (relation_table, self._module, self._name))
2333             self.invalidate_cache(cr, SUPERUSER_ID)
2334
2335     # checked version: for direct m2o starting from `self`
2336     def _m2o_add_foreign_key_checked(self, source_field, dest_model, ondelete):
2337         assert self.is_transient() or not dest_model.is_transient(), \
2338             'Many2One relationships from non-transient Model to TransientModel are forbidden'
2339         if self.is_transient() and not dest_model.is_transient():
2340             # TransientModel relationships to regular Models are annoying
2341             # usually because they could block deletion due to the FKs.
2342             # So unless stated otherwise we default them to ondelete=cascade.
2343             ondelete = ondelete or 'cascade'
2344         fk_def = (self._table, source_field, dest_model._table, ondelete or 'set null')
2345         self._foreign_keys.add(fk_def)
2346         _schema.debug("Table '%s': added foreign key '%s' with definition=REFERENCES \"%s\" ON DELETE %s", *fk_def)
2347
2348     # unchecked version: for custom cases, such as m2m relationships
2349     def _m2o_add_foreign_key_unchecked(self, source_table, source_field, dest_model, ondelete):
2350         fk_def = (source_table, source_field, dest_model._table, ondelete or 'set null')
2351         self._foreign_keys.add(fk_def)
2352         _schema.debug("Table '%s': added foreign key '%s' with definition=REFERENCES \"%s\" ON DELETE %s", *fk_def)
2353
2354     def _drop_constraint(self, cr, source_table, constraint_name):
2355         cr.execute("ALTER TABLE %s DROP CONSTRAINT %s" % (source_table,constraint_name))
2356
2357     def _m2o_fix_foreign_key(self, cr, source_table, source_field, dest_model, ondelete):
2358         # Find FK constraint(s) currently established for the m2o field,
2359         # and see whether they are stale or not
2360         cr.execute("""SELECT confdeltype as ondelete_rule, conname as constraint_name,
2361                              cl2.relname as foreign_table
2362                       FROM pg_constraint as con, pg_class as cl1, pg_class as cl2,
2363                            pg_attribute as att1, pg_attribute as att2
2364                       WHERE con.conrelid = cl1.oid
2365                         AND cl1.relname = %s
2366                         AND con.confrelid = cl2.oid
2367                         AND array_lower(con.conkey, 1) = 1
2368                         AND con.conkey[1] = att1.attnum
2369                         AND att1.attrelid = cl1.oid
2370                         AND att1.attname = %s
2371                         AND array_lower(con.confkey, 1) = 1
2372                         AND con.confkey[1] = att2.attnum
2373                         AND att2.attrelid = cl2.oid
2374                         AND att2.attname = %s
2375                         AND con.contype = 'f'""", (source_table, source_field, 'id'))
2376         constraints = cr.dictfetchall()
2377         if constraints:
2378             if len(constraints) == 1:
2379                 # Is it the right constraint?
2380                 cons, = constraints
2381                 if cons['ondelete_rule'] != POSTGRES_CONFDELTYPES.get((ondelete or 'set null').upper(), 'a')\
2382                     or cons['foreign_table'] != dest_model._table:
2383                     # Wrong FK: drop it and recreate
2384                     _schema.debug("Table '%s': dropping obsolete FK constraint: '%s'",
2385                                   source_table, cons['constraint_name'])
2386                     self._drop_constraint(cr, source_table, cons['constraint_name'])
2387                 else:
2388                     # it's all good, nothing to do!
2389                     return
2390             else:
2391                 # Multiple FKs found for the same field, drop them all, and re-create
2392                 for cons in constraints:
2393                     _schema.debug("Table '%s': dropping duplicate FK constraints: '%s'",
2394                                   source_table, cons['constraint_name'])
2395                     self._drop_constraint(cr, source_table, cons['constraint_name'])
2396
2397         # (re-)create the FK
2398         self._m2o_add_foreign_key_checked(source_field, dest_model, ondelete)
2399
2400
2401     def _set_default_value_on_column(self, cr, column_name, context=None):
2402         # ideally, we should use default_get(), but it fails due to ir.values
2403         # not being ready
2404
2405         # get default value
2406         default = self._defaults.get(column_name)
2407         if callable(default):
2408             default = default(self, cr, SUPERUSER_ID, context)
2409
2410         column = self._columns[column_name]
2411         ss = column._symbol_set
2412         db_default = ss[1](default)
2413         # Write default if non-NULL, except for booleans for which False means
2414         # the same as NULL - this saves us an expensive query on large tables.
2415         write_default = (db_default is not None if column._type != 'boolean'
2416                             else db_default)
2417         if write_default:
2418             _logger.debug("Table '%s': setting default value of new column %s to %r",
2419                           self._table, column_name, default)
2420             query = 'UPDATE "%s" SET "%s"=%s WHERE "%s" is NULL' % (
2421                 self._table, column_name, ss[0], column_name)
2422             cr.execute(query, (db_default,))
2423             # this is a disgrace
2424             cr.commit()
2425
2426     def _auto_init(self, cr, context=None):
2427         """
2428
2429         Call _field_create and, unless _auto is False:
2430
2431         - create the corresponding table in database for the model,
2432         - possibly add the parent columns in database,
2433         - possibly add the columns 'create_uid', 'create_date', 'write_uid',
2434           'write_date' in database if _log_access is True (the default),
2435         - report on database columns no more existing in _columns,
2436         - remove no more existing not null constraints,
2437         - alter existing database columns to match _columns,
2438         - create database tables to match _columns,
2439         - add database indices to match _columns,
2440         - save in self._foreign_keys a list a foreign keys to create (see
2441           _auto_end).
2442
2443         """
2444         self._foreign_keys = set()
2445         raise_on_invalid_object_name(self._name)
2446         if context is None:
2447             context = {}
2448         store_compute = False
2449         stored_fields = []              # new-style stored fields with compute
2450         todo_end = []
2451         update_custom_fields = context.get('update_custom_fields', False)
2452         self._field_create(cr, context=context)
2453         create = not self._table_exist(cr)
2454         if self._auto:
2455
2456             if create:
2457                 self._create_table(cr)
2458                 has_rows = False
2459             else:
2460                 cr.execute('SELECT 1 FROM "%s" LIMIT 1' % self._table)
2461                 has_rows = cr.rowcount
2462
2463             cr.commit()
2464             if self._parent_store:
2465                 if not self._parent_columns_exist(cr):
2466                     self._create_parent_columns(cr)
2467                     store_compute = True
2468
2469             self._check_removed_columns(cr, log=False)
2470
2471             # iterate on the "object columns"
2472             column_data = self._select_column_data(cr)
2473
2474             for k, f in self._columns.iteritems():
2475                 if k == 'id': # FIXME: maybe id should be a regular column?
2476                     continue
2477                 # Don't update custom (also called manual) fields
2478                 if f.manual and not update_custom_fields:
2479                     continue
2480
2481                 if isinstance(f, fields.one2many):
2482                     self._o2m_raise_on_missing_reference(cr, f)
2483
2484                 elif isinstance(f, fields.many2many):
2485                     self._m2m_raise_or_create_relation(cr, f)
2486
2487                 else:
2488                     res = column_data.get(k)
2489
2490                     # The field is not found as-is in database, try if it
2491                     # exists with an old name.
2492                     if not res and hasattr(f, 'oldname'):
2493                         res = column_data.get(f.oldname)
2494                         if res:
2495                             cr.execute('ALTER TABLE "%s" RENAME "%s" TO "%s"' % (self._table, f.oldname, k))
2496                             res['attname'] = k
2497                             column_data[k] = res
2498                             _schema.debug("Table '%s': renamed column '%s' to '%s'",
2499                                 self._table, f.oldname, k)
2500
2501                     # The field already exists in database. Possibly
2502                     # change its type, rename it, drop it or change its
2503                     # constraints.
2504                     if res:
2505                         f_pg_type = res['typname']
2506                         f_pg_size = res['size']
2507                         f_pg_notnull = res['attnotnull']
2508                         if isinstance(f, fields.function) and not f.store and\
2509                                 not getattr(f, 'nodrop', False):
2510                             _logger.info('column %s (%s) converted to a function, removed from table %s',
2511                                          k, f.string, self._table)
2512                             cr.execute('ALTER TABLE "%s" DROP COLUMN "%s" CASCADE' % (self._table, k))
2513                             cr.commit()
2514                             _schema.debug("Table '%s': dropped column '%s' with cascade",
2515                                 self._table, k)
2516                             f_obj_type = None
2517                         else:
2518                             f_obj_type = get_pg_type(f) and get_pg_type(f)[0]
2519
2520                         if f_obj_type:
2521                             ok = False
2522                             casts = [
2523                                 ('text', 'char', pg_varchar(f.size), '::%s' % pg_varchar(f.size)),
2524                                 ('varchar', 'text', 'TEXT', ''),
2525                                 ('int4', 'float', get_pg_type(f)[1], '::'+get_pg_type(f)[1]),
2526                                 ('date', 'datetime', 'TIMESTAMP', '::TIMESTAMP'),
2527                                 ('timestamp', 'date', 'date', '::date'),
2528                                 ('numeric', 'float', get_pg_type(f)[1], '::'+get_pg_type(f)[1]),
2529                                 ('float8', 'float', get_pg_type(f)[1], '::'+get_pg_type(f)[1]),
2530                             ]
2531                             if f_pg_type == 'varchar' and f._type == 'char' and f_pg_size and (f.size is None or f_pg_size < f.size):
2532                                 try:
2533                                     with cr.savepoint():
2534                                         cr.execute('ALTER TABLE "%s" ALTER COLUMN "%s" TYPE %s' % (self._table, k, pg_varchar(f.size)))
2535                                 except psycopg2.NotSupportedError:
2536                                     # In place alter table cannot be done because a view is depending of this field.
2537                                     # Do a manual copy. This will drop the view (that will be recreated later)
2538                                     cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO temp_change_size' % (self._table, k))
2539                                     cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, pg_varchar(f.size)))
2540                                     cr.execute('UPDATE "%s" SET "%s"=temp_change_size::%s' % (self._table, k, pg_varchar(f.size)))
2541                                     cr.execute('ALTER TABLE "%s" DROP COLUMN temp_change_size CASCADE' % (self._table,))
2542                                 cr.commit()
2543                                 _schema.debug("Table '%s': column '%s' (type varchar) changed size from %s to %s",
2544                                     self._table, k, f_pg_size or 'unlimited', f.size or 'unlimited')
2545                             for c in casts:
2546                                 if (f_pg_type==c[0]) and (f._type==c[1]):
2547                                     if f_pg_type != f_obj_type:
2548                                         ok = True
2549                                         cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO __temp_type_cast' % (self._table, k))
2550                                         cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, c[2]))
2551                                         cr.execute(('UPDATE "%s" SET "%s"= __temp_type_cast'+c[3]) % (self._table, k))
2552                                         cr.execute('ALTER TABLE "%s" DROP COLUMN  __temp_type_cast CASCADE' % (self._table,))
2553                                         cr.commit()
2554                                         _schema.debug("Table '%s': column '%s' changed type from %s to %s",
2555                                             self._table, k, c[0], c[1])
2556                                     break
2557
2558                             if f_pg_type != f_obj_type:
2559                                 if not ok:
2560                                     i = 0
2561                                     while True:
2562                                         newname = k + '_moved' + str(i)
2563                                         cr.execute("SELECT count(1) FROM pg_class c,pg_attribute a " \
2564                                             "WHERE c.relname=%s " \
2565                                             "AND a.attname=%s " \
2566                                             "AND c.oid=a.attrelid ", (self._table, newname))
2567                                         if not cr.fetchone()[0]:
2568                                             break
2569                                         i += 1
2570                                     if f_pg_notnull:
2571                                         cr.execute('ALTER TABLE "%s" ALTER COLUMN "%s" DROP NOT NULL' % (self._table, k))
2572                                     cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO "%s"' % (self._table, k, newname))
2573                                     cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, get_pg_type(f)[1]))
2574                                     cr.execute("COMMENT ON COLUMN %s.\"%s\" IS %%s" % (self._table, k), (f.string,))
2575                                     _schema.debug("Table '%s': column '%s' has changed type (DB=%s, def=%s), data moved to column %s !",
2576                                         self._table, k, f_pg_type, f._type, newname)
2577
2578                             # if the field is required and hasn't got a NOT NULL constraint
2579                             if f.required and f_pg_notnull == 0:
2580                                 if has_rows:
2581                                     self._set_default_value_on_column(cr, k, context=context)
2582                                 # add the NOT NULL constraint
2583                                 try:
2584                                     cr.execute('ALTER TABLE "%s" ALTER COLUMN "%s" SET NOT NULL' % (self._table, k), log_exceptions=False)
2585                                     cr.commit()
2586                                     _schema.debug("Table '%s': column '%s': added NOT NULL constraint",
2587                                         self._table, k)
2588                                 except Exception:
2589                                     msg = "Table '%s': unable to set a NOT NULL constraint on column '%s' !\n"\
2590                                         "If you want to have it, you should update the records and execute manually:\n"\
2591                                         "ALTER TABLE %s ALTER COLUMN %s SET NOT NULL"
2592                                     _schema.warning(msg, self._table, k, self._table, k)
2593                                 cr.commit()
2594                             elif not f.required and f_pg_notnull == 1:
2595                                 cr.execute('ALTER TABLE "%s" ALTER COLUMN "%s" DROP NOT NULL' % (self._table, k))
2596                                 cr.commit()
2597                                 _schema.debug("Table '%s': column '%s': dropped NOT NULL constraint",
2598                                     self._table, k)
2599                             # Verify index
2600                             indexname = '%s_%s_index' % (self._table, k)
2601                             cr.execute("SELECT indexname FROM pg_indexes WHERE indexname = %s and tablename = %s", (indexname, self._table))
2602                             res2 = cr.dictfetchall()
2603                             if not res2 and f.select:
2604                                 cr.execute('CREATE INDEX "%s_%s_index" ON "%s" ("%s")' % (self._table, k, self._table, k))
2605                                 cr.commit()
2606                                 if f._type == 'text':
2607                                     # FIXME: for fields.text columns we should try creating GIN indexes instead (seems most suitable for an ERP context)
2608                                     msg = "Table '%s': Adding (b-tree) index for %s column '%s'."\
2609                                         "This is probably useless (does not work for fulltext search) and prevents INSERTs of long texts"\
2610                                         " because there is a length limit for indexable btree values!\n"\
2611                                         "Use a search view instead if you simply want to make the field searchable."
2612                                     _schema.warning(msg, self._table, f._type, k)
2613                             if res2 and not f.select:
2614                                 cr.execute('DROP INDEX "%s_%s_index"' % (self._table, k))
2615                                 cr.commit()
2616                                 msg = "Table '%s': dropping index for column '%s' of type '%s' as it is not required anymore"
2617                                 _schema.debug(msg, self._table, k, f._type)
2618
2619                             if isinstance(f, fields.many2one) or (isinstance(f, fields.function) and f._type == 'many2one' and f.store):
2620                                 dest_model = self.pool[f._obj]
2621                                 if dest_model._auto and dest_model._table != 'ir_actions':
2622                                     self._m2o_fix_foreign_key(cr, self._table, k, dest_model, f.ondelete)
2623
2624                     # The field doesn't exist in database. Create it if necessary.
2625                     else:
2626                         if not isinstance(f, fields.function) or f.store:
2627                             # add the missing field
2628                             cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, get_pg_type(f)[1]))
2629                             cr.execute("COMMENT ON COLUMN %s.\"%s\" IS %%s" % (self._table, k), (f.string,))
2630                             _schema.debug("Table '%s': added column '%s' with definition=%s",
2631                                 self._table, k, get_pg_type(f)[1])
2632
2633                             # initialize it
2634                             if has_rows:
2635                                 self._set_default_value_on_column(cr, k, context=context)
2636
2637                             # remember the functions to call for the stored fields
2638                             if isinstance(f, fields.function):
2639                                 order = 10
2640                                 if f.store is not True: # i.e. if f.store is a dict
2641                                     order = f.store[f.store.keys()[0]][2]
2642                                 todo_end.append((order, self._update_store, (f, k)))
2643
2644                             # remember new-style stored fields with compute method
2645                             if k in self._fields and self._fields[k].depends:
2646                                 stored_fields.append(self._fields[k])
2647
2648                             # and add constraints if needed
2649                             if isinstance(f, fields.many2one) or (isinstance(f, fields.function) and f._type == 'many2one' and f.store):
2650                                 if f._obj not in self.pool:
2651                                     raise except_orm('Programming Error', 'There is no reference available for %s' % (f._obj,))
2652                                 dest_model = self.pool[f._obj]
2653                                 ref = dest_model._table
2654                                 # ir_actions is inherited so foreign key doesn't work on it
2655                                 if dest_model._auto and ref != 'ir_actions':
2656                                     self._m2o_add_foreign_key_checked(k, dest_model, f.ondelete)
2657                             if f.select:
2658                                 cr.execute('CREATE INDEX "%s_%s_index" ON "%s" ("%s")' % (self._table, k, self._table, k))
2659                             if f.required:
2660                                 try:
2661                                     cr.commit()
2662                                     cr.execute('ALTER TABLE "%s" ALTER COLUMN "%s" SET NOT NULL' % (self._table, k))
2663                                     _schema.debug("Table '%s': column '%s': added a NOT NULL constraint",
2664                                         self._table, k)
2665                                 except Exception:
2666                                     msg = "WARNING: unable to set column %s of table %s not null !\n"\
2667                                         "Try to re-run: openerp-server --update=module\n"\
2668                                         "If it doesn't work, update records and execute manually:\n"\
2669                                         "ALTER TABLE %s ALTER COLUMN %s SET NOT NULL"
2670                                     _logger.warning(msg, k, self._table, self._table, k, exc_info=True)
2671                             cr.commit()
2672
2673         else:
2674             cr.execute("SELECT relname FROM pg_class WHERE relkind IN ('r','v') AND relname=%s", (self._table,))
2675             create = not bool(cr.fetchone())
2676
2677         cr.commit()     # start a new transaction
2678
2679         if self._auto:
2680             self._add_sql_constraints(cr)
2681
2682         if create:
2683             self._execute_sql(cr)
2684
2685         if store_compute:
2686             self._parent_store_compute(cr)
2687             cr.commit()
2688
2689         if stored_fields:
2690             # trigger computation of new-style stored fields with a compute
2691             def func(cr):
2692                 _logger.info("Storing computed values of %s fields %s",
2693                     self._name, ', '.join(sorted(f.name for f in stored_fields)))
2694                 recs = self.browse(cr, SUPERUSER_ID, [], {'active_test': False})
2695                 recs = recs.search([])
2696                 if recs:
2697                     map(recs._recompute_todo, stored_fields)
2698                     recs.recompute()
2699
2700             todo_end.append((1000, func, ()))
2701
2702         return todo_end
2703
2704     def _auto_end(self, cr, context=None):
2705         """ Create the foreign keys recorded by _auto_init. """
2706         for t, k, r, d in self._foreign_keys:
2707             cr.execute('ALTER TABLE "%s" ADD FOREIGN KEY ("%s") REFERENCES "%s" ON DELETE %s' % (t, k, r, d))
2708             self._save_constraint(cr, "%s_%s_fkey" % (t, k), 'f')
2709         cr.commit()
2710         del self._foreign_keys
2711
2712
2713     def _table_exist(self, cr):
2714         cr.execute("SELECT relname FROM pg_class WHERE relkind IN ('r','v') AND relname=%s", (self._table,))
2715         return cr.rowcount
2716
2717
2718     def _create_table(self, cr):
2719         cr.execute('CREATE TABLE "%s" (id SERIAL NOT NULL, PRIMARY KEY(id))' % (self._table,))
2720         cr.execute(("COMMENT ON TABLE \"%s\" IS %%s" % self._table), (self._description,))
2721         _schema.debug("Table '%s': created", self._table)
2722
2723
2724     def _parent_columns_exist(self, cr):
2725         cr.execute("""SELECT c.relname
2726             FROM pg_class c, pg_attribute a
2727             WHERE c.relname=%s AND a.attname=%s AND c.oid=a.attrelid
2728             """, (self._table, 'parent_left'))
2729         return cr.rowcount
2730
2731
2732     def _create_parent_columns(self, cr):
2733         cr.execute('ALTER TABLE "%s" ADD COLUMN "parent_left" INTEGER' % (self._table,))
2734         cr.execute('ALTER TABLE "%s" ADD COLUMN "parent_right" INTEGER' % (self._table,))
2735         if 'parent_left' not in self._columns:
2736             _logger.error('create a column parent_left on object %s: fields.integer(\'Left Parent\', select=1)',
2737                           self._table)
2738             _schema.debug("Table '%s': added column '%s' with definition=%s",
2739                 self._table, 'parent_left', 'INTEGER')
2740         elif not self._columns['parent_left'].select:
2741             _logger.error('parent_left column on object %s must be indexed! Add select=1 to the field definition)',
2742                           self._table)
2743         if 'parent_right' not in self._columns:
2744             _logger.error('create a column parent_right on object %s: fields.integer(\'Right Parent\', select=1)',
2745                           self._table)
2746             _schema.debug("Table '%s': added column '%s' with definition=%s",
2747                 self._table, 'parent_right', 'INTEGER')
2748         elif not self._columns['parent_right'].select:
2749             _logger.error('parent_right column on object %s must be indexed! Add select=1 to the field definition)',
2750                           self._table)
2751         if self._columns[self._parent_name].ondelete not in ('cascade', 'restrict'):
2752             _logger.error("The column %s on object %s must be set as ondelete='cascade' or 'restrict'",
2753                           self._parent_name, self._name)
2754
2755         cr.commit()
2756
2757
2758     def _select_column_data(self, cr):
2759         # attlen is the number of bytes necessary to represent the type when
2760         # the type has a fixed size. If the type has a varying size attlen is
2761         # -1 and atttypmod is the size limit + 4, or -1 if there is no limit.
2762         cr.execute("SELECT c.relname,a.attname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,t.typname,CASE WHEN a.attlen=-1 THEN (CASE WHEN a.atttypmod=-1 THEN 0 ELSE a.atttypmod-4 END) ELSE a.attlen END as size " \
2763            "FROM pg_class c,pg_attribute a,pg_type t " \
2764            "WHERE c.relname=%s " \
2765            "AND c.oid=a.attrelid " \
2766            "AND a.atttypid=t.oid", (self._table,))
2767         return dict(map(lambda x: (x['attname'], x),cr.dictfetchall()))
2768
2769
2770     def _o2m_raise_on_missing_reference(self, cr, f):
2771         # TODO this check should be a method on fields.one2many.
2772         if f._obj in self.pool:
2773             other = self.pool[f._obj]
2774             # TODO the condition could use fields_get_keys().
2775             if f._fields_id not in other._columns.keys():
2776                 if f._fields_id not in other._inherit_fields.keys():
2777                     raise except_orm('Programming Error', "There is no reference field '%s' found for '%s'" % (f._fields_id, f._obj,))
2778
2779     def _m2m_raise_or_create_relation(self, cr, f):
2780         m2m_tbl, col1, col2 = f._sql_names(self)
2781         # do not create relations for custom fields as they do not belong to a module
2782         # they will be automatically removed when dropping the corresponding ir.model.field
2783         # table name for custom relation all starts with x_, see __init__
2784         if not m2m_tbl.startswith('x_'):
2785             self._save_relation_table(cr, m2m_tbl)
2786         cr.execute("SELECT relname FROM pg_class WHERE relkind IN ('r','v') AND relname=%s", (m2m_tbl,))
2787         if not cr.dictfetchall():
2788             if f._obj not in self.pool:
2789                 raise except_orm('Programming Error', 'Many2Many destination model does not exist: `%s`' % (f._obj,))
2790             dest_model = self.pool[f._obj]
2791             ref = dest_model._table
2792             cr.execute('CREATE TABLE "%s" ("%s" INTEGER NOT NULL, "%s" INTEGER NOT NULL, UNIQUE("%s","%s"))' % (m2m_tbl, col1, col2, col1, col2))
2793             # create foreign key references with ondelete=cascade, unless the targets are SQL views
2794             cr.execute("SELECT relkind FROM pg_class WHERE relkind IN ('v') AND relname=%s", (ref,))
2795             if not cr.fetchall():
2796                 self._m2o_add_foreign_key_unchecked(m2m_tbl, col2, dest_model, 'cascade')
2797             cr.execute("SELECT relkind FROM pg_class WHERE relkind IN ('v') AND relname=%s", (self._table,))
2798             if not cr.fetchall():
2799                 self._m2o_add_foreign_key_unchecked(m2m_tbl, col1, self, 'cascade')
2800
2801             cr.execute('CREATE INDEX "%s_%s_index" ON "%s" ("%s")' % (m2m_tbl, col1, m2m_tbl, col1))
2802             cr.execute('CREATE INDEX "%s_%s_index" ON "%s" ("%s")' % (m2m_tbl, col2, m2m_tbl, col2))
2803             cr.execute("COMMENT ON TABLE \"%s\" IS 'RELATION BETWEEN %s AND %s'" % (m2m_tbl, self._table, ref))
2804             cr.commit()
2805             _schema.debug("Create table '%s': m2m relation between '%s' and '%s'", m2m_tbl, self._table, ref)
2806
2807
2808     def _add_sql_constraints(self, cr):
2809         """
2810
2811         Modify this model's database table constraints so they match the one in
2812         _sql_constraints.
2813
2814         """
2815         def unify_cons_text(txt):
2816             return txt.lower().replace(', ',',').replace(' (','(')
2817
2818         for (key, con, _) in self._sql_constraints:
2819             conname = '%s_%s' % (self._table, key)
2820
2821             self._save_constraint(cr, conname, 'u')
2822             cr.execute("SELECT conname, pg_catalog.pg_get_constraintdef(oid, true) as condef FROM pg_constraint where conname=%s", (conname,))
2823             existing_constraints = cr.dictfetchall()
2824             sql_actions = {
2825                 'drop': {
2826                     'execute': False,
2827                     'query': 'ALTER TABLE "%s" DROP CONSTRAINT "%s"' % (self._table, conname, ),
2828                     'msg_ok': "Table '%s': dropped constraint '%s'. Reason: its definition changed from '%%s' to '%s'" % (
2829                         self._table, conname, con),
2830                     'msg_err': "Table '%s': unable to drop \'%s\' constraint !" % (self._table, con),
2831                     'order': 1,
2832                 },
2833                 'add': {
2834                     'execute': False,
2835                     'query': 'ALTER TABLE "%s" ADD CONSTRAINT "%s" %s' % (self._table, conname, con,),
2836                     'msg_ok': "Table '%s': added constraint '%s' with definition=%s" % (self._table, conname, con),
2837                     'msg_err': "Table '%s': unable to add \'%s\' constraint !\n If you want to have it, you should update the records and execute manually:\n%%s" % (
2838                         self._table, con),
2839                     'order': 2,
2840                 },
2841             }
2842
2843             if not existing_constraints:
2844                 # constraint does not exists:
2845                 sql_actions['add']['execute'] = True
2846                 sql_actions['add']['msg_err'] = sql_actions['add']['msg_err'] % (sql_actions['add']['query'], )
2847             elif unify_cons_text(con) not in [unify_cons_text(item['condef']) for item in existing_constraints]:
2848                 # constraint exists but its definition has changed:
2849                 sql_actions['drop']['execute'] = True
2850                 sql_actions['drop']['msg_ok'] = sql_actions['drop']['msg_ok'] % (existing_constraints[0]['condef'].lower(), )
2851                 sql_actions['add']['execute'] = True
2852                 sql_actions['add']['msg_err'] = sql_actions['add']['msg_err'] % (sql_actions['add']['query'], )
2853
2854             # we need to add the constraint:
2855             sql_actions = [item for item in sql_actions.values()]
2856             sql_actions.sort(key=lambda x: x['order'])
2857             for sql_action in [action for action in sql_actions if action['execute']]:
2858                 try:
2859                     cr.execute(sql_action['query'])
2860                     cr.commit()
2861                     _schema.debug(sql_action['msg_ok'])
2862                 except:
2863                     _schema.warning(sql_action['msg_err'])
2864                     cr.rollback()
2865
2866
2867     def _execute_sql(self, cr):
2868         """ Execute the SQL code from the _sql attribute (if any)."""
2869         if hasattr(self, "_sql"):
2870             for line in self._sql.split(';'):
2871                 line2 = line.replace('\n', '').strip()
2872                 if line2:
2873                     cr.execute(line2)
2874                     cr.commit()
2875
2876     #
2877     # Update objects that uses this one to update their _inherits fields
2878     #
2879
2880     @classmethod
2881     def _inherits_reload_src(cls):
2882         """ Recompute the _inherit_fields mapping on each _inherits'd child model."""
2883         for model in cls.pool.values():
2884             if cls._name in model._inherits:
2885                 model._inherits_reload()
2886
2887     @classmethod
2888     def _inherits_reload(cls):
2889         """ Recompute the _inherit_fields mapping.
2890
2891         This will also call itself on each inherits'd child model.
2892
2893         """
2894         res = {}
2895         for table in cls._inherits:
2896             other = cls.pool[table]
2897             for col in other._columns.keys():
2898                 res[col] = (table, cls._inherits[table], other._columns[col], table)
2899             for col in other._inherit_fields.keys():
2900                 res[col] = (table, cls._inherits[table], other._inherit_fields[col][2], other._inherit_fields[col][3])
2901         cls._inherit_fields = res
2902         cls._all_columns = cls._get_column_infos()
2903
2904         # interface columns with new-style fields
2905         for attr, column in cls._columns.items():
2906             if attr not in cls._fields:
2907                 cls._add_field(attr, column.to_field())
2908
2909         # interface inherited fields with new-style fields (note that the
2910         # reverse order is for being consistent with _all_columns above)
2911         for parent_model, parent_field in reversed(cls._inherits.items()):
2912             for attr, field in cls.pool[parent_model]._fields.iteritems():
2913                 if attr not in cls._fields:
2914                     cls._add_field(attr, field.new(
2915                         inherited=True,
2916                         related=(parent_field, attr),
2917                         related_sudo=False,
2918                     ))
2919
2920         cls._inherits_reload_src()
2921
2922     @classmethod
2923     def _get_column_infos(cls):
2924         """Returns a dict mapping all fields names (direct fields and
2925            inherited field via _inherits) to a ``column_info`` struct
2926            giving detailed columns """
2927         result = {}
2928         # do not inverse for loops, since local fields may hide inherited ones!
2929         for k, (parent, m2o, col, original_parent) in cls._inherit_fields.iteritems():
2930             result[k] = fields.column_info(k, col, parent, m2o, original_parent)
2931         for k, col in cls._columns.iteritems():
2932             result[k] = fields.column_info(k, col)
2933         return result
2934
2935     @classmethod
2936     def _inherits_check(cls):
2937         for table, field_name in cls._inherits.items():
2938             if field_name not in cls._columns:
2939                 _logger.info('Missing many2one field definition for _inherits reference "%s" in "%s", using default one.', field_name, cls._name)
2940                 cls._columns[field_name] = fields.many2one(table, string="Automatically created field to link to parent %s" % table,
2941                                                              required=True, ondelete="cascade")
2942             elif not cls._columns[field_name].required or cls._columns[field_name].ondelete.lower() not in ("cascade", "restrict"):
2943                 _logger.warning('Field definition for _inherits reference "%s" in "%s" must be marked as "required" with ondelete="cascade" or "restrict", forcing it to required + cascade.', field_name, cls._name)
2944                 cls._columns[field_name].required = True
2945                 cls._columns[field_name].ondelete = "cascade"
2946
2947         # reflect fields with delegate=True in dictionary cls._inherits
2948         for field in cls._fields.itervalues():
2949             if field.type == 'many2one' and not field.related and field.delegate:
2950                 if not field.required:
2951                     _logger.warning("Field %s with delegate=True must be required.", field)
2952                     field.required = True
2953                 if field.ondelete.lower() not in ('cascade', 'restrict'):
2954                     field.ondelete = 'cascade'
2955                 cls._inherits[field.comodel_name] = field.name
2956
2957     @api.model
2958     def _prepare_setup_fields(self):
2959         """ Prepare the setup of fields once the models have been loaded. """
2960         for field in self._fields.itervalues():
2961             field.reset()
2962
2963     @api.model
2964     def _setup_fields(self, partial=False):
2965         """ Setup the fields (dependency triggers, etc). """
2966         for field in self._fields.itervalues():
2967             if partial and field.manual and \
2968                     field.relational and \
2969                     (field.comodel_name not in self.pool or \
2970                      (field.type == 'one2many' and field.inverse_name not in self.pool[field.comodel_name]._fields)):
2971                 # do not set up manual fields that refer to unknown models
2972                 continue
2973             field.setup(self.env)
2974
2975         # group fields by compute to determine field.computed_fields
2976         fields_by_compute = defaultdict(list)
2977         for field in self._fields.itervalues():
2978             if field.compute:
2979                 field.computed_fields = fields_by_compute[field.compute]
2980                 field.computed_fields.append(field)
2981             else:
2982                 field.computed_fields = []
2983
2984     def fields_get(self, cr, user, allfields=None, context=None, write_access=True):
2985         """ fields_get([fields])
2986
2987         Return the definition of each field.
2988
2989         The returned value is a dictionary (indiced by field name) of
2990         dictionaries. The _inherits'd fields are included. The string, help,
2991         and selection (if present) attributes are translated.
2992
2993         :param cr: database cursor
2994         :param user: current user id
2995         :param allfields: list of fields
2996         :param context: context arguments, like lang, time zone
2997         :return: dictionary of field dictionaries, each one describing a field of the business object
2998         :raise AccessError: * if user has no create/write rights on the requested object
2999
3000         """
3001         recs = self.browse(cr, user, [], context)
3002
3003         res = {}
3004         for fname, field in self._fields.iteritems():
3005             if allfields and fname not in allfields:
3006                 continue
3007             if not field.setup_done:
3008                 continue
3009             if field.groups and not recs.user_has_groups(field.groups):
3010                 continue
3011             res[fname] = field.get_description(recs.env)
3012
3013         # if user cannot create or modify records, make all fields readonly
3014         has_access = functools.partial(recs.check_access_rights, raise_exception=False)
3015         if not (has_access('write') or has_access('create')):
3016             for description in res.itervalues():
3017                 description['readonly'] = True
3018                 description['states'] = {}
3019
3020         return res
3021
3022     def get_empty_list_help(self, cr, user, help, context=None):
3023         """ Generic method giving the help message displayed when having
3024             no result to display in a list or kanban view. By default it returns
3025             the help given in parameter that is generally the help message
3026             defined in the action.
3027         """
3028         return help
3029
3030     def check_field_access_rights(self, cr, user, operation, fields, context=None):
3031         """
3032         Check the user access rights on the given fields. This raises Access
3033         Denied if the user does not have the rights. Otherwise it returns the
3034         fields (as is if the fields is not falsy, or the readable/writable
3035         fields if fields is falsy).
3036         """
3037         if user == SUPERUSER_ID:
3038             return fields or list(self._fields)
3039
3040         def valid(fname):
3041             """ determine whether user has access to field `fname` """
3042             field = self._fields.get(fname)
3043             if field and field.groups:
3044                 return self.user_has_groups(cr, user, groups=field.groups, context=context)
3045             else:
3046                 return True
3047
3048         if not fields:
3049             fields = filter(valid, self._fields)
3050         else:
3051             invalid_fields = set(filter(lambda name: not valid(name), fields))
3052             if invalid_fields:
3053                 _logger.warning('Access Denied by ACLs for operation: %s, uid: %s, model: %s, fields: %s',
3054                     operation, user, self._name, ', '.join(invalid_fields))
3055                 raise AccessError(
3056                     _('The requested operation cannot be completed due to security restrictions. '
3057                     'Please contact your system administrator.\n\n(Document type: %s, Operation: %s)') % \
3058                     (self._description, operation))
3059
3060         return fields
3061
3062     # add explicit old-style implementation to read()
3063     @api.v7
3064     def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'):
3065         records = self.browse(cr, user, ids, context)
3066         result = BaseModel.read(records, fields, load=load)
3067         return result if isinstance(ids, list) else (bool(result) and result[0])
3068
3069     # new-style implementation of read()
3070     @api.v8
3071     def read(self, fields=None, load='_classic_read'):
3072         """ read([fields])
3073
3074         Reads the requested fields for the records in `self`, low-level/RPC
3075         method. In Python code, prefer :meth:`~.browse`.
3076
3077         :param fields: list of field names to return (default is all fields)
3078         :return: a list of dictionaries mapping field names to their values,
3079                  with one dictionary per record
3080         :raise AccessError: if user has no read rights on some of the given
3081                 records
3082         """
3083         # check access rights
3084         self.check_access_rights('read')
3085         fields = self.check_field_access_rights('read', fields)
3086
3087         # split fields into stored and computed fields
3088         stored, computed = [], []
3089         for name in fields:
3090             if name in self._columns:
3091                 stored.append(name)
3092             elif name in self._fields:
3093                 computed.append(name)
3094             else:
3095                 _logger.warning("%s.read() with unknown field '%s'", self._name, name)
3096
3097         # fetch stored fields from the database to the cache
3098         self._read_from_database(stored)
3099
3100         # retrieve results from records; this takes values from the cache and
3101         # computes remaining fields
3102         result = []
3103         name_fields = [(name, self._fields[name]) for name in (stored + computed)]
3104         use_name_get = (load == '_classic_read')
3105         for record in self:
3106             try:
3107                 values = {'id': record.id}
3108                 for name, field in name_fields:
3109                     values[name] = field.convert_to_read(record[name], use_name_get)
3110                 result.append(values)
3111             except MissingError:
3112                 pass
3113
3114         return result
3115
3116     @api.multi
3117     def _prefetch_field(self, field):
3118         """ Read from the database in order to fetch `field` (:class:`Field`
3119             instance) for `self` in cache.
3120         """
3121         # fetch the records of this model without field_name in their cache
3122         records = self._in_cache_without(field)
3123
3124         if len(records) > PREFETCH_MAX:
3125             records = records[:PREFETCH_MAX] | self
3126
3127         # determine which fields can be prefetched
3128         if not self.env.in_draft and \
3129                 self._context.get('prefetch_fields', True) and \
3130                 self._columns[field.name]._prefetch:
3131             # prefetch all classic and many2one fields that the user can access
3132             fnames = {fname
3133                 for fname, fcolumn in self._columns.iteritems()
3134                 if fcolumn._prefetch
3135                 if not fcolumn.groups or self.user_has_groups(fcolumn.groups)
3136             }
3137         else:
3138             fnames = {field.name}
3139
3140         # important: never prefetch fields to recompute!
3141         get_recs_todo = self.env.field_todo
3142         for fname in list(fnames):
3143             if get_recs_todo(self._fields[fname]):
3144                 if fname == field.name:
3145                     records -= get_recs_todo(field)
3146                 else:
3147                     fnames.discard(fname)
3148
3149         # fetch records with read()
3150         assert self in records and field.name in fnames
3151         result = []
3152         try:
3153             result = records.read(list(fnames), load='_classic_write')
3154         except AccessError:
3155             pass
3156
3157         # check the cache, and update it if necessary
3158         if not self._cache.contains(field):
3159             for values in result:
3160                 record = self.browse(values.pop('id'))
3161                 record._cache.update(record._convert_to_cache(values, validate=False))
3162             if not self._cache.contains(field):
3163                 e = AccessError("No value found for %s.%s" % (self, field.name))
3164                 self._cache[field] = FailedValue(e)
3165
3166     @api.multi
3167     def _read_from_database(self, field_names):
3168         """ Read the given fields of the records in `self` from the database,
3169             and store them in cache. Access errors are also stored in cache.
3170         """
3171         env = self.env
3172         cr, user, context = env.args
3173
3174         # FIXME: The query construction needs to be rewritten using the internal Query
3175         # object, as in search(), to avoid ambiguous column references when
3176         # reading/sorting on a table that is auto_joined to another table with
3177         # common columns (e.g. the magical columns)
3178
3179         # Construct a clause for the security rules.
3180         # 'tables' holds the list of tables necessary for the SELECT, including
3181         # the ir.rule clauses, and contains at least self._table.
3182         rule_clause, rule_params, tables = env['ir.rule'].domain_get(self._name, 'read')
3183
3184         # determine the fields that are stored as columns in self._table
3185         fields_pre = [f for f in field_names if self._columns[f]._classic_write]
3186
3187         # we need fully-qualified column names in case len(tables) > 1
3188         def qualify(f):
3189             if isinstance(self._columns.get(f), fields.binary) and \
3190                     context.get('bin_size_%s' % f, context.get('bin_size')):
3191                 # PG 9.2 introduces conflicting pg_size_pretty(numeric) -> need ::cast 
3192                 return 'pg_size_pretty(length(%s."%s")::bigint) as "%s"' % (self._table, f, f)
3193             else:
3194                 return '%s."%s"' % (self._table, f)
3195         qual_names = map(qualify, set(fields_pre + ['id']))
3196
3197         query = """ SELECT %(qual_names)s FROM %(tables)s
3198                     WHERE %(table)s.id IN %%s AND (%(extra)s)
3199                     ORDER BY %(order)s
3200                 """ % {
3201                     'qual_names': ",".join(qual_names),
3202                     'tables': ",".join(tables),
3203                     'table': self._table,
3204                     'extra': " OR ".join(rule_clause) if rule_clause else "TRUE",
3205                     'order': self._parent_order or self._order,
3206                 }
3207
3208         result = []
3209         for sub_ids in cr.split_for_in_conditions(self.ids):
3210             cr.execute(query, [tuple(sub_ids)] + rule_params)
3211             result.extend(cr.dictfetchall())
3212
3213         ids = [vals['id'] for vals in result]
3214
3215         if ids:
3216             # translate the fields if necessary
3217             if context.get('lang'):
3218                 ir_translation = env['ir.translation']
3219                 for f in fields_pre:
3220                     if self._columns[f].translate:
3221                         #TODO: optimize out of this loop
3222                         res_trans = ir_translation._get_ids(
3223                             '%s,%s' % (self._name, f), 'model', context['lang'], ids)
3224                         for vals in result:
3225                             vals[f] = res_trans.get(vals['id'], False) or vals[f]
3226
3227             # apply the symbol_get functions of the fields we just read
3228             for f in fields_pre:
3229                 symbol_get = self._columns[f]._symbol_get
3230                 if symbol_get:
3231                     for vals in result:
3232                         vals[f] = symbol_get(vals[f])
3233
3234             # store result in cache for POST fields
3235             for vals in result:
3236                 record = self.browse(vals['id'])
3237                 record._cache.update(record._convert_to_cache(vals, validate=False))
3238
3239             # determine the fields that must be processed now
3240             fields_post = [f for f in field_names if not self._columns[f]._classic_write]
3241
3242             # Compute POST fields, grouped by multi
3243             by_multi = defaultdict(list)
3244             for f in fields_post:
3245                 by_multi[self._columns[f]._multi].append(f)
3246
3247             for multi, fs in by_multi.iteritems():
3248                 if multi:
3249                     res2 = self._columns[fs[0]].get(cr, self._model, ids, fs, user, context=context, values=result)
3250                     assert res2 is not None, \
3251                         'The function field "%s" on the "%s" model returned None\n' \
3252                         '(a dictionary was expected).' % (fs[0], self._name)
3253                     for vals in result:
3254                         # TOCHECK : why got string instend of dict in python2.6
3255                         # if isinstance(res2[vals['id']], str): res2[vals['id']] = eval(res2[vals['id']])
3256                         multi_fields = res2.get(vals['id'], {})
3257                         if multi_fields:
3258                             for f in fs:
3259                                 vals[f] = multi_fields.get(f, [])
3260                 else:
3261                     for f in fs:
3262                         res2 = self._columns[f].get(cr, self._model, ids, f, user, context=context, values=result)
3263                         for vals in result:
3264                             if res2:
3265                                 vals[f] = res2[vals['id']]
3266                             else:
3267                                 vals[f] = []
3268
3269         # Warn about deprecated fields now that fields_pre and fields_post are computed
3270         for f in field_names:
3271             column = self._columns[f]
3272             if column.deprecated:
3273                 _logger.warning('Field %s.%s is deprecated: %s', self._name, f, column.deprecated)
3274
3275         # store result in cache
3276         for vals in result:
3277             record = self.browse(vals.pop('id'))
3278             record._cache.update(record._convert_to_cache(vals, validate=False))
3279
3280         # store failed values in cache for the records that could not be read
3281         fetched = self.browse(ids)
3282         missing = self - fetched
3283         if missing:
3284             extras = fetched - self
3285             if extras:
3286                 raise AccessError(
3287                     _("Database fetch misses ids ({}) and has extra ids ({}), may be caused by a type incoherence in a previous request").format(
3288                         ', '.join(map(repr, missing._ids)),
3289                         ', '.join(map(repr, extras._ids)),
3290                     ))
3291             # store an access error exception in existing records
3292             exc = AccessError(
3293                 _('The requested operation cannot be completed due to security restrictions. Please contact your system administrator.\n\n(Document type: %s, Operation: %s)') % \
3294                 (self._name, 'read')
3295             )
3296             forbidden = missing.exists()
3297             forbidden._cache.update(FailedValue(exc))
3298             # store a missing error exception in non-existing records
3299             exc = MissingError(
3300                 _('One of the documents you are trying to access has been deleted, please try again after refreshing.')
3301             )
3302             (missing - forbidden)._cache.update(FailedValue(exc))
3303
3304     @api.multi
3305     def get_metadata(self):
3306         """
3307         Returns some metadata about the given records.
3308
3309         :return: list of ownership dictionaries for each requested record
3310         :rtype: list of dictionaries with the following keys:
3311
3312                     * id: object id
3313                     * create_uid: user who created the record
3314                     * create_date: date when the record was created
3315                     * write_uid: last user who changed the record
3316                     * write_date: date of the last change to the record
3317                     * xmlid: XML ID to use to refer to this record (if there is one), in format ``module.name``
3318         """
3319         fields = ['id']
3320         if self._log_access:
3321             fields += ['create_uid', 'create_date', 'write_uid', 'write_date']
3322         quoted_table = '"%s"' % self._table
3323         fields_str = ",".join('%s.%s' % (quoted_table, field) for field in fields)
3324         query = '''SELECT %s, __imd.module, __imd.name
3325                    FROM %s LEFT JOIN ir_model_data __imd
3326                        ON (__imd.model = %%s and __imd.res_id = %s.id)
3327                    WHERE %s.id IN %%s''' % (fields_str, quoted_table, quoted_table, quoted_table)
3328         self._cr.execute(query, (self._name, tuple(self.ids)))
3329         res = self._cr.dictfetchall()
3330
3331         uids = set(r[k] for r in res for k in ['write_uid', 'create_uid'] if r.get(k))
3332         names = dict(self.env['res.users'].browse(uids).name_get())
3333
3334         for r in res:
3335             for key in r:
3336                 value = r[key] = r[key] or False
3337                 if key in ('write_uid', 'create_uid') and value in names:
3338                     r[key] = (value, names[value])
3339             r['xmlid'] = ("%(module)s.%(name)s" % r) if r['name'] else False
3340             del r['name'], r['module']
3341         return res
3342
3343     def _check_concurrency(self, cr, ids, context):
3344         if not context:
3345             return
3346         if not (context.get(self.CONCURRENCY_CHECK_FIELD) and self._log_access):
3347             return
3348         check_clause = "(id = %s AND %s < COALESCE(write_date, create_date, (now() at time zone 'UTC'))::timestamp)"
3349         for sub_ids in cr.split_for_in_conditions(ids):
3350             ids_to_check = []
3351             for id in sub_ids:
3352                 id_ref = "%s,%s" % (self._name, id)
3353                 update_date = context[self.CONCURRENCY_CHECK_FIELD].pop(id_ref, None)
3354                 if update_date:
3355                     ids_to_check.extend([id, update_date])
3356             if not ids_to_check:
3357                 continue
3358             cr.execute("SELECT id FROM %s WHERE %s" % (self._table, " OR ".join([check_clause]*(len(ids_to_check)/2))), tuple(ids_to_check))
3359             res = cr.fetchone()
3360             if res:
3361                 # mention the first one only to keep the error message readable
3362                 raise except_orm('ConcurrencyException', _('A document was modified since you last viewed it (%s:%d)') % (self._description, res[0]))
3363
3364     def _check_record_rules_result_count(self, cr, uid, ids, result_ids, operation, context=None):
3365         """Verify the returned rows after applying record rules matches
3366            the length of `ids`, and raise an appropriate exception if it does not.
3367         """
3368         if context is None:
3369             context = {}
3370         ids, result_ids = set(ids), set(result_ids)
3371         missing_ids = ids - result_ids
3372         if missing_ids:
3373             # Attempt to distinguish record rule restriction vs deleted records,
3374             # to provide a more specific error message - check if the missinf
3375             cr.execute('SELECT id FROM ' + self._table + ' WHERE id IN %s', (tuple(missing_ids),))
3376             forbidden_ids = [x[0] for x in cr.fetchall()]
3377             if forbidden_ids:
3378                 # the missing ids are (at least partially) hidden by access rules
3379                 if uid == SUPERUSER_ID:
3380                     return
3381                 _logger.warning('Access Denied by record rules for operation: %s on record ids: %r, uid: %s, model: %s', operation, forbidden_ids, uid, self._name)
3382                 raise except_orm(_('Access Denied'),
3383                                  _('The requested operation cannot be completed due to security restrictions. Please contact your system administrator.\n\n(Document type: %s, Operation: %s)') % \
3384                                     (self._description, operation))
3385             else:
3386                 # If we get here, the missing_ids are not in the database
3387                 if operation in ('read','unlink'):
3388                     # No need to warn about deleting an already deleted record.
3389                     # And no error when reading a record that was deleted, to prevent spurious
3390                     # errors for non-transactional search/read sequences coming from clients
3391                     return
3392                 _logger.warning('Failed operation on deleted record(s): %s, uid: %s, model: %s', operation, uid, self._name)
3393                 raise except_orm(_('Missing document(s)'),
3394                                  _('One of the documents you are trying to access has been deleted, please try again after refreshing.'))
3395
3396
3397     def check_access_rights(self, cr, uid, operation, raise_exception=True): # no context on purpose.
3398         """Verifies that the operation given by ``operation`` is allowed for the user
3399            according to the access rights."""
3400         return self.pool.get('ir.model.access').check(cr, uid, self._name, operation, raise_exception)
3401
3402     def check_access_rule(self, cr, uid, ids, operation, context=None):
3403         """Verifies that the operation given by ``operation`` is allowed for the user
3404            according to ir.rules.
3405
3406            :param operation: one of ``write``, ``unlink``
3407            :raise except_orm: * if current ir.rules do not permit this operation.
3408            :return: None if the operation is allowed
3409         """
3410         if uid == SUPERUSER_ID:
3411             return
3412
3413         if self.is_transient():
3414             # Only one single implicit access rule for transient models: owner only!
3415             # This is ok to hardcode because we assert that TransientModels always
3416             # have log_access enabled so that the create_uid column is always there.
3417             # And even with _inherits, these fields are always present in the local
3418             # table too, so no need for JOINs.
3419             cr.execute("""SELECT distinct create_uid
3420                           FROM %s
3421                           WHERE id IN %%s""" % self._table, (tuple(ids),))
3422             uids = [x[0] for x in cr.fetchall()]
3423             if len(uids) != 1 or uids[0] != uid:
3424                 raise except_orm(_('Access Denied'),
3425                                  _('For this kind of document, you may only access records you created yourself.\n\n(Document type: %s)') % (self._description,))
3426         else:
3427             where_clause, where_params, tables = self.pool.get('ir.rule').domain_get(cr, uid, self._name, operation, context=context)
3428             if where_clause:
3429                 where_clause = ' and ' + ' and '.join(where_clause)
3430                 for sub_ids in cr.split_for_in_conditions(ids):
3431                     cr.execute('SELECT ' + self._table + '.id FROM ' + ','.join(tables) +
3432                                ' WHERE ' + self._table + '.id IN %s' + where_clause,
3433                                [sub_ids] + where_params)
3434                     returned_ids = [x['id'] for x in cr.dictfetchall()]
3435                     self._check_record_rules_result_count(cr, uid, sub_ids, returned_ids, operation, context=context)
3436
3437     def create_workflow(self, cr, uid, ids, context=None):
3438         """Create a workflow instance for each given record IDs."""
3439         from openerp import workflow
3440         for res_id in ids:
3441             workflow.trg_create(uid, self._name, res_id, cr)
3442         # self.invalidate_cache(cr, uid, context=context) ?
3443         return True
3444
3445     def delete_workflow(self, cr, uid, ids, context=None):
3446         """Delete the workflow instances bound to the given record IDs."""
3447         from openerp import workflow
3448         for res_id in ids:
3449             workflow.trg_delete(uid, self._name, res_id, cr)
3450         self.invalidate_cache(cr, uid, context=context)
3451         return True
3452
3453     def step_workflow(self, cr, uid, ids, context=None):
3454         """Reevaluate the workflow instances of the given record IDs."""
3455         from openerp import workflow
3456         for res_id in ids:
3457             workflow.trg_write(uid, self._name, res_id, cr)
3458         # self.invalidate_cache(cr, uid, context=context) ?
3459         return True
3460
3461     def signal_workflow(self, cr, uid, ids, signal, context=None):
3462         """Send given workflow signal and return a dict mapping ids to workflow results"""
3463         from openerp import workflow
3464         result = {}
3465         for res_id in ids:
3466             result[res_id] = workflow.trg_validate(uid, self._name, res_id, signal, cr)
3467         # self.invalidate_cache(cr, uid, context=context) ?
3468         return result
3469
3470     def redirect_workflow(self, cr, uid, old_new_ids, context=None):
3471         """ Rebind the workflow instance bound to the given 'old' record IDs to
3472             the given 'new' IDs. (``old_new_ids`` is a list of pairs ``(old, new)``.
3473         """
3474         from openerp import workflow
3475         for old_id, new_id in old_new_ids:
3476             workflow.trg_redirect(uid, self._name, old_id, new_id, cr)
3477         self.invalidate_cache(cr, uid, context=context)
3478         return True
3479
3480     def unlink(self, cr, uid, ids, context=None):
3481         """ unlink()
3482
3483         Deletes the records of the current set
3484
3485         :raise AccessError: * if user has no unlink rights on the requested object
3486                             * if user tries to bypass access rules for unlink on the requested object
3487         :raise UserError: if the record is default property for other records
3488
3489         """
3490         if not ids:
3491             return True
3492         if isinstance(ids, (int, long)):
3493             ids = [ids]
3494
3495         result_store = self._store_get_values(cr, uid, ids, self._all_columns.keys(), context)
3496
3497         # for recomputing new-style fields
3498         recs = self.browse(cr, uid, ids, context)
3499         recs.modified(self._fields)
3500
3501         self._check_concurrency(cr, ids, context)
3502
3503         self.check_access_rights(cr, uid, 'unlink')
3504
3505         ir_property = self.pool.get('ir.property')
3506
3507         # Check if the records are used as default properties.
3508         domain = [('res_id', '=', False),
3509                   ('value_reference', 'in', ['%s,%s' % (self._name, i) for i in ids]),
3510                  ]
3511         if ir_property.search(cr, uid, domain, context=context):
3512             raise except_orm(_('Error'), _('Unable to delete this document because it is used as a default property'))
3513
3514         # Delete the records' properties.
3515         property_ids = ir_property.search(cr, uid, [('res_id', 'in', ['%s,%s' % (self._name, i) for i in ids])], context=context)
3516         ir_property.unlink(cr, uid, property_ids, context=context)
3517
3518         self.delete_workflow(cr, uid, ids, context=context)
3519
3520         self.check_access_rule(cr, uid, ids, 'unlink', context=context)
3521         pool_model_data = self.pool.get('ir.model.data')
3522         ir_values_obj = self.pool.get('ir.values')
3523         ir_attachment_obj = self.pool.get('ir.attachment')
3524         for sub_ids in cr.split_for_in_conditions(ids):
3525             cr.execute('delete from ' + self._table + ' ' \
3526                        'where id IN %s', (sub_ids,))
3527
3528             # Removing the ir_model_data reference if the record being deleted is a record created by xml/csv file,
3529             # as these are not connected with real database foreign keys, and would be dangling references.
3530             # Note: following steps performed as admin to avoid access rights restrictions, and with no context
3531             #       to avoid possible side-effects during admin calls.
3532             # Step 1. Calling unlink of ir_model_data only for the affected IDS
3533             reference_ids = pool_model_data.search(cr, SUPERUSER_ID, [('res_id','in',list(sub_ids)),('model','=',self._name)])
3534             # Step 2. Marching towards the real deletion of referenced records
3535             if reference_ids:
3536                 pool_model_data.unlink(cr, SUPERUSER_ID, reference_ids)
3537
3538             # For the same reason, removing the record relevant to ir_values
3539             ir_value_ids = ir_values_obj.search(cr, uid,
3540                     ['|',('value','in',['%s,%s' % (self._name, sid) for sid in sub_ids]),'&',('res_id','in',list(sub_ids)),('model','=',self._name)],
3541                     context=context)
3542             if ir_value_ids:
3543                 ir_values_obj.unlink(cr, uid, ir_value_ids, context=context)
3544
3545             # For the same reason, removing the record relevant to ir_attachment
3546             # The search is performed with sql as the search method of ir_attachment is overridden to hide attachments of deleted records
3547             cr.execute('select id from ir_attachment where res_model = %s and res_id in %s', (self._name, sub_ids))
3548             ir_attachment_ids = [ir_attachment[0] for ir_attachment in cr.fetchall()]
3549             if ir_attachment_ids:
3550                 ir_attachment_obj.unlink(cr, uid, ir_attachment_ids, context=context)
3551
3552         # invalidate the *whole* cache, since the orm does not handle all
3553         # changes made in the database, like cascading delete!
3554         recs.invalidate_cache()
3555
3556         for order, obj_name, store_ids, fields in result_store:
3557             if obj_name == self._name:
3558                 effective_store_ids = set(store_ids) - set(ids)
3559             else:
3560                 effective_store_ids = store_ids
3561             if effective_store_ids:
3562                 obj = self.pool[obj_name]
3563                 cr.execute('select id from '+obj._table+' where id IN %s', (tuple(effective_store_ids),))
3564                 rids = map(lambda x: x[0], cr.fetchall())
3565                 if rids:
3566                     obj._store_set_values(cr, uid, rids, fields, context)
3567
3568         # recompute new-style fields
3569         recs.recompute()
3570
3571         return True
3572
3573     #
3574     # TODO: Validate
3575     #
3576     @api.multi
3577     def write(self, vals):
3578         """ write(vals)
3579
3580         Updates all records in the current set with the provided values.
3581
3582         :param dict vals: fields to update and the value to set on them e.g::
3583
3584                 {'foo': 1, 'bar': "Qux"}
3585
3586             will set the field ``foo`` to ``1`` and the field ``bar`` to
3587             ``"Qux"`` if those are valid (otherwise it will trigger an error).
3588
3589         :raise AccessError: * if user has no write rights on the requested object
3590                             * if user tries to bypass access rules for write on the requested object
3591         :raise ValidateError: if user tries to enter invalid value for a field that is not in selection
3592         :raise UserError: if a loop would be created in a hierarchy of objects a result of the operation (such as setting an object as its own parent)
3593
3594         .. _openerp/models/relationals/format:
3595
3596         .. note:: Relational fields use a special "commands" format to manipulate their values
3597
3598             This format is a list of command triplets executed sequentially,
3599             possible command triplets are:
3600
3601             ``(0, _, values: dict)``
3602                 links to a new record created from the provided values
3603             ``(1, id, values: dict)``
3604                 updates the already-linked record of id ``id`` with the
3605                 provided ``values``
3606             ``(2, id, _)``
3607                 unlinks and deletes the linked record of id ``id``
3608             ``(3, id, _)``
3609                 unlinks the linked record of id ``id`` without deleting it
3610             ``(4, id, _)``
3611                 links to an existing record of id ``id``
3612             ``(5, _, _)``
3613                 unlinks all records in the relation, equivalent to using
3614                 the command ``3`` on every linked record
3615             ``(6, _, ids)``
3616                 replaces the existing list of linked records by the provoded
3617                 ones, equivalent to using ``5`` then ``4`` for each id in
3618                 ``ids``)
3619
3620             (in command triplets, ``_`` values are ignored and can be
3621             anything, generally ``0`` or ``False``)
3622
3623             Any command can be used on :class:`~openerp.fields.Many2many`,
3624             only ``0``, ``1`` and ``2`` can be used on
3625             :class:`~openerp.fields.One2many`.
3626         """
3627         if not self:
3628             return True
3629
3630         self._check_concurrency(self._ids)
3631         self.check_access_rights('write')
3632
3633         # No user-driven update of these columns
3634         for field in itertools.chain(MAGIC_COLUMNS, ('parent_left', 'parent_right')):
3635             vals.pop(field, None)
3636
3637         # split up fields into old-style and pure new-style ones
3638         old_vals, new_vals, unknown = {}, {}, []
3639         for key, val in vals.iteritems():
3640             if key in self._columns:
3641                 old_vals[key] = val
3642             elif key in self._fields:
3643                 new_vals[key] = val
3644             else:
3645                 unknown.append(key)
3646
3647         if unknown:
3648             _logger.warning("%s.write() with unknown fields: %s", self._name, ', '.join(sorted(unknown)))
3649
3650         # write old-style fields with (low-level) method _write
3651         if old_vals:
3652             self._write(old_vals)
3653
3654         # put the values of pure new-style fields into cache, and inverse them
3655         if new_vals:
3656             for record in self:
3657                 record._cache.update(record._convert_to_cache(new_vals, update=True))
3658             for key in new_vals:
3659                 self._fields[key].determine_inverse(self)
3660
3661         return True
3662
3663     def _write(self, cr, user, ids, vals, context=None):
3664         # low-level implementation of write()
3665         if not context:
3666             context = {}
3667
3668         readonly = None
3669         self.check_field_access_rights(cr, user, 'write', vals.keys())
3670         deleted_related = defaultdict(list)
3671         for field in vals.keys():
3672             fobj = None
3673             if field in self._columns:
3674                 fobj = self._columns[field]
3675             elif field in self._inherit_fields:
3676                 fobj = self._inherit_fields[field][2]
3677             if not fobj:
3678                 continue
3679             if fobj._type in ['one2many', 'many2many'] and vals[field]:
3680                 for wtuple in vals[field]:
3681                     if isinstance(wtuple, (tuple, list)) and wtuple[0] == 2:
3682                         deleted_related[fobj._obj].append(wtuple[1])
3683             groups = fobj.write
3684
3685             if groups:
3686                 edit = False
3687                 for group in groups:
3688                     module = group.split(".")[0]
3689                     grp = group.split(".")[1]
3690                     cr.execute("select count(*) from res_groups_users_rel where gid IN (select res_id from ir_model_data where name=%s and module=%s and model=%s) and uid=%s", \
3691                                (grp, module, 'res.groups', user))
3692                     readonly = cr.fetchall()
3693                     if readonly[0][0] >= 1:
3694                         edit = True
3695                         break
3696
3697                 if not edit:
3698                     vals.pop(field)
3699
3700         result = self._store_get_values(cr, user, ids, vals.keys(), context) or []
3701
3702         # for recomputing new-style fields
3703         recs = self.browse(cr, user, ids, context)
3704         modified_fields = list(vals)
3705         if self._log_access:
3706             modified_fields += ['write_date', 'write_uid']
3707         recs.modified(modified_fields)
3708
3709         parents_changed = []
3710         parent_order = self._parent_order or self._order
3711         if self._parent_store and (self._parent_name in vals) and not context.get('defer_parent_store_computation'):
3712             # The parent_left/right computation may take up to
3713             # 5 seconds. No need to recompute the values if the
3714             # parent is the same.
3715             # Note: to respect parent_order, nodes must be processed in
3716             # order, so ``parents_changed`` must be ordered properly.
3717             parent_val = vals[self._parent_name]
3718             if parent_val:
3719                 query = "SELECT id FROM %s WHERE id IN %%s AND (%s != %%s OR %s IS NULL) ORDER BY %s" % \
3720                                 (self._table, self._parent_name, self._parent_name, parent_order)
3721                 cr.execute(query, (tuple(ids), parent_val))
3722             else:
3723                 query = "SELECT id FROM %s WHERE id IN %%s AND (%s IS NOT NULL) ORDER BY %s" % \
3724                                 (self._table, self._parent_name, parent_order)
3725                 cr.execute(query, (tuple(ids),))
3726             parents_changed = map(operator.itemgetter(0), cr.fetchall())
3727
3728         upd0 = []
3729         upd1 = []
3730         upd_todo = []
3731         updend = []
3732         direct = []
3733         totranslate = context.get('lang', False) and (context['lang'] != 'en_US')
3734         for field in vals:
3735             field_column = self._all_columns.get(field) and self._all_columns.get(field).column
3736             if field_column and field_column.deprecated:
3737                 _logger.warning('Field %s.%s is deprecated: %s', self._name, field, field_column.deprecated)
3738             if field in self._columns:
3739                 if self._columns[field]._classic_write and not (hasattr(self._columns[field], '_fnct_inv')):
3740                     if (not totranslate) or not self._columns[field].translate:
3741                         upd0.append('"'+field+'"='+self._columns[field]._symbol_set[0])
3742                         upd1.append(self._columns[field]._symbol_set[1](vals[field]))
3743                     direct.append(field)
3744                 else:
3745                     upd_todo.append(field)
3746             else:
3747                 updend.append(field)
3748             if field in self._columns \
3749                     and hasattr(self._columns[field], 'selection') \
3750                     and vals[field]:
3751                 self._check_selection_field_value(cr, user, field, vals[field], context=context)
3752
3753         if self._log_access:
3754             upd0.append('write_uid=%s')
3755             upd0.append("write_date=(now() at time zone 'UTC')")
3756             upd1.append(user)
3757             direct.append('write_uid')
3758             direct.append('write_date')
3759
3760         if len(upd0):
3761             self.check_access_rule(cr, user, ids, 'write', context=context)
3762             for sub_ids in cr.split_for_in_conditions(ids):
3763                 cr.execute('update ' + self._table + ' set ' + ','.join(upd0) + ' ' \
3764                            'where id IN %s', upd1 + [sub_ids])
3765                 if cr.rowcount != len(sub_ids):
3766                     raise MissingError(_('One of the records you are trying to modify has already been deleted (Document type: %s).') % self._description)
3767
3768             if totranslate:
3769                 # TODO: optimize
3770                 for f in direct:
3771                     if self._columns[f].translate:
3772                         src_trans = self.pool[self._name].read(cr, user, ids, [f])[0][f]
3773                         if not src_trans:
3774                             src_trans = vals[f]
3775                             # Inserting value to DB
3776                             context_wo_lang = dict(context, lang=None)
3777                             self.write(cr, user, ids, {f: vals[f]}, context=context_wo_lang)
3778                         self.pool.get('ir.translation')._set_ids(cr, user, self._name+','+f, 'model', context['lang'], ids, vals[f], src_trans)
3779
3780         # invalidate and mark new-style fields to recompute; do this before
3781         # setting other fields, because it can require the value of computed
3782         # fields, e.g., a one2many checking constraints on records
3783         recs.modified(direct)
3784
3785         # call the 'set' method of fields which are not classic_write
3786         upd_todo.sort(lambda x, y: self._columns[x].priority-self._columns[y].priority)
3787
3788         # default element in context must be removed when call a one2many or many2many
3789         rel_context = context.copy()
3790         for c in context.items():
3791             if c[0].startswith('default_'):
3792                 del rel_context[c[0]]
3793
3794         for field in upd_todo:
3795             for id in ids:
3796                 result += self._columns[field].set(cr, self, id, field, vals[field], user, context=rel_context) or []
3797
3798         # for recomputing new-style fields
3799         recs.modified(upd_todo)
3800
3801         unknown_fields = updend[:]
3802         for table in self._inherits:
3803             col = self._inherits[table]
3804             nids = []
3805             for sub_ids in cr.split_for_in_conditions(ids):
3806                 cr.execute('select distinct "'+col+'" from "'+self._table+'" ' \
3807                            'where id IN %s', (sub_ids,))
3808                 nids.extend([x[0] for x in cr.fetchall()])
3809
3810             v = {}
3811             for val in updend:
3812                 if self._inherit_fields[val][0] == table:
3813                     v[val] = vals[val]
3814                     unknown_fields.remove(val)
3815             if v:
3816                 self.pool[table].write(cr, user, nids, v, context)
3817
3818         if unknown_fields:
3819             _logger.warning(
3820                 'No such field(s) in model %s: %s.',
3821                 self._name, ', '.join(unknown_fields))
3822
3823         # check Python constraints
3824         recs._validate_fields(vals)
3825
3826         # TODO: use _order to set dest at the right position and not first node of parent
3827         # We can't defer parent_store computation because the stored function
3828         # fields that are computer may refer (directly or indirectly) to
3829         # parent_left/right (via a child_of domain)
3830         if parents_changed:
3831             if self.pool._init:
3832                 self.pool._init_parent[self._name] = True
3833             else:
3834                 order = self._parent_order or self._order
3835                 parent_val = vals[self._parent_name]
3836                 if parent_val:
3837                     clause, params = '%s=%%s' % (self._parent_name,), (parent_val,)
3838                 else:
3839                     clause, params = '%s IS NULL' % (self._parent_name,), ()
3840
3841                 for id in parents_changed:
3842                     cr.execute('SELECT parent_left, parent_right FROM %s WHERE id=%%s' % (self._table,), (id,))
3843                     pleft, pright = cr.fetchone()
3844                     distance = pright - pleft + 1
3845
3846                     # Positions of current siblings, to locate proper insertion point;
3847                     # this can _not_ be fetched outside the loop, as it needs to be refreshed
3848                     # after each update, in case several nodes are sequentially inserted one
3849                     # next to the other (i.e computed incrementally)
3850                     cr.execute('SELECT parent_right, id FROM %s WHERE %s ORDER BY %s' % (self._table, clause, parent_order), params)
3851                     parents = cr.fetchall()
3852
3853                     # Find Position of the element
3854                     position = None
3855                     for (parent_pright, parent_id) in parents:
3856                         if parent_id == id:
3857                             break
3858                         position = parent_pright and parent_pright + 1 or 1
3859
3860                     # It's the first node of the parent
3861                     if not position:
3862                         if not parent_val:
3863                             position = 1
3864                         else:
3865                             cr.execute('select parent_left from '+self._table+' where id=%s', (parent_val,))
3866                             position = cr.fetchone()[0] + 1
3867
3868                     if pleft < position <= pright:
3869                         raise except_orm(_('UserError'), _('Recursivity Detected.'))
3870
3871                     if pleft < position:
3872                         cr.execute('update '+self._table+' set parent_left=parent_left+%s where parent_left>=%s', (distance, position))
3873                         cr.execute('update '+self._table+' set parent_right=parent_right+%s where parent_right>=%s', (distance, position))
3874                         cr.execute('update '+self._table+' set parent_left=parent_left+%s, parent_right=parent_right+%s where parent_left>=%s and parent_left<%s', (position-pleft, position-pleft, pleft, pright))
3875                     else:
3876                         cr.execute('update '+self._table+' set parent_left=parent_left+%s where parent_left>=%s', (distance, position))
3877                         cr.execute('update '+self._table+' set parent_right=parent_right+%s where parent_right>=%s', (distance, position))
3878                         cr.execute('update '+self._table+' set parent_left=parent_left-%s, parent_right=parent_right-%s where parent_left>=%s and parent_left<%s', (pleft-position+distance, pleft-position+distance, pleft+distance, pright+distance))
3879                     recs.invalidate_cache(['parent_left', 'parent_right'])
3880
3881         result += self._store_get_values(cr, user, ids, vals.keys(), context)
3882         result.sort()
3883
3884         done = {}
3885         for order, model_name, ids_to_update, fields_to_recompute in result:
3886             key = (model_name, tuple(fields_to_recompute))
3887             done.setdefault(key, {})
3888             # avoid to do several times the same computation
3889             todo = []
3890             for id in ids_to_update:
3891                 if id not in done[key]:
3892                     done[key][id] = True
3893                     if id not in deleted_related[model_name]:
3894                         todo.append(id)
3895             self.pool[model_name]._store_set_values(cr, user, todo, fields_to_recompute, context)
3896
3897         # recompute new-style fields
3898         if context.get('recompute', True):
3899             recs.recompute()
3900
3901         self.step_workflow(cr, user, ids, context=context)
3902         return True
3903
3904     #
3905     # TODO: Should set perm to user.xxx
3906     #
3907     @api.model
3908     @api.returns('self', lambda value: value.id)
3909     def create(self, vals):
3910         """ create(vals) -> record
3911
3912         Creates a new record for the model.
3913
3914         The new record is initialized using the values from ``vals`` and
3915         if necessary those from :meth:`~.default_get`.
3916
3917         :param dict vals:
3918             values for the model's fields, as a dictionary::
3919
3920                 {'field_name': field_value, ...}
3921
3922             see :meth:`~.write` for details
3923         :return: new record created
3924         :raise AccessError: * if user has no create rights on the requested object
3925                             * if user tries to bypass access rules for create on the requested object
3926         :raise ValidateError: if user tries to enter invalid value for a field that is not in selection
3927         :raise UserError: if a loop would be created in a hierarchy of objects a result of the operation (such as setting an object as its own parent)
3928         """
3929         self.check_access_rights('create')
3930
3931         # add missing defaults, and drop fields that may not be set by user
3932         vals = self._add_missing_default_values(vals)
3933         for field in itertools.chain(MAGIC_COLUMNS, ('parent_left', 'parent_right')):
3934             vals.pop(field, None)
3935
3936         # split up fields into old-style and pure new-style ones
3937         old_vals, new_vals, unknown = {}, {}, []
3938         for key, val in vals.iteritems():
3939             if key in self._all_columns:
3940                 old_vals[key] = val
3941             elif key in self._fields:
3942                 new_vals[key] = val
3943             else:
3944                 unknown.append(key)
3945
3946         if unknown:
3947             _logger.warning("%s.create() with unknown fields: %s", self._name, ', '.join(sorted(unknown)))
3948
3949         # create record with old-style fields
3950         record = self.browse(self._create(old_vals))
3951
3952         # put the values of pure new-style fields into cache, and inverse them
3953         record._cache.update(record._convert_to_cache(new_vals))
3954         for key in new_vals:
3955             self._fields[key].determine_inverse(record)
3956
3957         return record
3958
3959     def _create(self, cr, user, vals, context=None):
3960         # low-level implementation of create()
3961         if not context:
3962             context = {}
3963
3964         if self.is_transient():
3965             self._transient_vacuum(cr, user)
3966
3967         tocreate = {}
3968         for v in self._inherits:
3969             if self._inherits[v] not in vals:
3970                 tocreate[v] = {}
3971             else:
3972                 tocreate[v] = {'id': vals[self._inherits[v]]}
3973
3974         updates = [
3975             # list of column assignments defined as tuples like:
3976             #   (column_name, format_string, column_value)
3977             #   (column_name, sql_formula)
3978             # Those tuples will be used by the string formatting for the INSERT
3979             # statement below.
3980             ('id', "nextval('%s')" % self._sequence),
3981         ]
3982
3983         upd_todo = []
3984         unknown_fields = []
3985         for v in vals.keys():
3986             if v in self._inherit_fields and v not in self._columns:
3987                 (table, col, col_detail, original_parent) = self._inherit_fields[v]
3988                 tocreate[table][v] = vals[v]
3989                 del vals[v]
3990             else:
3991                 if (v not in self._inherit_fields) and (v not in self._columns):
3992                     del vals[v]
3993                     unknown_fields.append(v)
3994         if unknown_fields:
3995             _logger.warning(
3996                 'No such field(s) in model %s: %s.',
3997                 self._name, ', '.join(unknown_fields))
3998
3999         for table in tocreate:
4000             if self._inherits[table] in vals:
4001                 del vals[self._inherits[table]]
4002
4003             record_id = tocreate[table].pop('id', None)
4004
4005             if record_id is None or not record_id:
4006                 record_id = self.pool[table].create(cr, user, tocreate[table], context=context)
4007             else:
4008                 self.pool[table].write(cr, user, [record_id], tocreate[table], context=context)
4009
4010             updates.append((self._inherits[table], '%s', record_id))
4011
4012         #Start : Set bool fields to be False if they are not touched(to make search more powerful)
4013         bool_fields = [x for x in self._columns.keys() if self._columns[x]._type=='boolean']
4014
4015         for bool_field in bool_fields:
4016             if bool_field not in vals:
4017                 vals[bool_field] = False
4018         #End
4019         for field in vals.keys():
4020             fobj = None
4021             if field in self._columns:
4022                 fobj = self._columns[field]
4023             else:
4024                 fobj = self._inherit_fields[field][2]
4025             if not fobj:
4026                 continue
4027             groups = fobj.write
4028             if groups:
4029                 edit = False
4030                 for group in groups:
4031                     module = group.split(".")[0]
4032                     grp = group.split(".")[1]
4033                     cr.execute("select count(*) from res_groups_users_rel where gid IN (select res_id from ir_model_data where name='%s' and module='%s' and model='%s') and uid=%s" % \
4034                                (grp, module, 'res.groups', user))
4035                     readonly = cr.fetchall()
4036                     if readonly[0][0] >= 1:
4037                         edit = True
4038                         break
4039                     elif readonly[0][0] == 0:
4040                         edit = False
4041                     else:
4042                         edit = False
4043
4044                 if not edit:
4045                     vals.pop(field)
4046         for field in vals:
4047             current_field = self._columns[field]
4048             if current_field._classic_write:
4049                 updates.append((field, '%s', current_field._symbol_set[1](vals[field])))
4050
4051                 #for the function fields that receive a value, we set them directly in the database
4052                 #(they may be required), but we also need to trigger the _fct_inv()
4053                 if (hasattr(current_field, '_fnct_inv')) and not isinstance(current_field, fields.related):
4054                     #TODO: this way to special case the related fields is really creepy but it shouldn't be changed at
4055                     #one week of the release candidate. It seems the only good way to handle correctly this is to add an
4056                     #attribute to make a field `really readonly´ and thus totally ignored by the create()... otherwise
4057                     #if, for example, the related has a default value (for usability) then the fct_inv is called and it
4058                     #may raise some access rights error. Changing this is a too big change for now, and is thus postponed
4059                     #after the release but, definitively, the behavior shouldn't be different for related and function
4060                     #fields.
4061                     upd_todo.append(field)
4062             else:
4063                 #TODO: this `if´ statement should be removed because there is no good reason to special case the fields
4064                 #related. See the above TODO comment for further explanations.
4065                 if not isinstance(current_field, fields.related):
4066                     upd_todo.append(field)
4067             if field in self._columns \
4068                     and hasattr(current_field, 'selection') \
4069                     and vals[field]:
4070                 self._check_selection_field_value(cr, user, field, vals[field], context=context)
4071         if self._log_access:
4072             updates.append(('create_uid', '%s', user))
4073             updates.append(('write_uid', '%s', user))
4074             updates.append(('create_date', "(now() at time zone 'UTC')"))
4075             updates.append(('write_date', "(now() at time zone 'UTC')"))
4076
4077         # the list of tuples used in this formatting corresponds to
4078         # tuple(field_name, format, value)
4079         # In some case, for example (id, create_date, write_date) we does not
4080         # need to read the third value of the tuple, because the real value is
4081         # encoded in the second value (the format).
4082         cr.execute(
4083             """INSERT INTO "%s" (%s) VALUES(%s) RETURNING id""" % (
4084                 self._table,
4085                 ', '.join('"%s"' % u[0] for u in updates),
4086                 ', '.join(u[1] for u in updates)
4087             ),
4088             tuple([u[2] for u in updates if len(u) > 2])
4089         )
4090
4091         id_new, = cr.fetchone()
4092         recs = self.browse(cr, user, id_new, context)
4093
4094         if self._parent_store and not context.get('defer_parent_store_computation'):
4095             if self.pool._init:
4096                 self.pool._init_parent[self._name] = True
4097             else:
4098                 parent = vals.get(self._parent_name, False)
4099                 if parent:
4100                     cr.execute('select parent_right from '+self._table+' where '+self._parent_name+'=%s order by '+(self._parent_order or self._order), (parent,))
4101                     pleft_old = None
4102                     result_p = cr.fetchall()
4103                     for (pleft,) in result_p:
4104                         if not pleft:
4105                             break
4106                         pleft_old = pleft
4107                     if not pleft_old:
4108                         cr.execute('select parent_left from '+self._table+' where id=%s', (parent,))
4109                         pleft_old = cr.fetchone()[0]
4110                     pleft = pleft_old
4111                 else:
4112                     cr.execute('select max(parent_right) from '+self._table)
4113                     pleft = cr.fetchone()[0] or 0
4114                 cr.execute('update '+self._table+' set parent_left=parent_left+2 where parent_left>%s', (pleft,))
4115                 cr.execute('update '+self._table+' set parent_right=parent_right+2 where parent_right>%s', (pleft,))
4116                 cr.execute('update '+self._table+' set parent_left=%s,parent_right=%s where id=%s', (pleft+1, pleft+2, id_new))
4117                 recs.invalidate_cache(['parent_left', 'parent_right'])
4118
4119         # invalidate and mark new-style fields to recompute; do this before
4120         # setting other fields, because it can require the value of computed
4121         # fields, e.g., a one2many checking constraints on records
4122         recs.modified([u[0] for u in updates])
4123
4124         # call the 'set' method of fields which are not classic_write
4125         upd_todo.sort(lambda x, y: self._columns[x].priority-self._columns[y].priority)
4126
4127         # default element in context must be remove when call a one2many or many2many
4128         rel_context = context.copy()
4129         for c in context.items():
4130             if c[0].startswith('default_'):
4131                 del rel_context[c[0]]
4132
4133         result = []
4134         for field in upd_todo:
4135             result += self._columns[field].set(cr, self, id_new, field, vals[field], user, rel_context) or []
4136
4137         # for recomputing new-style fields
4138         recs.modified(upd_todo)
4139
4140         # check Python constraints
4141         recs._validate_fields(vals)
4142
4143         if context.get('recompute', True):
4144             result += self._store_get_values(cr, user, [id_new],
4145                 list(set(vals.keys() + self._inherits.values())),
4146                 context)
4147             result.sort()
4148             done = []
4149             for order, model_name, ids, fields2 in result:
4150                 if not (model_name, ids, fields2) in done:
4151                     self.pool[model_name]._store_set_values(cr, user, ids, fields2, context)
4152                     done.append((model_name, ids, fields2))
4153             # recompute new-style fields
4154             recs.recompute()
4155
4156         if self._log_create and context.get('recompute', True):
4157             message = self._description + \
4158                 " '" + \
4159                 self.name_get(cr, user, [id_new], context=context)[0][1] + \
4160                 "' " + _("created.")
4161             self.log(cr, user, id_new, message, True, context=context)
4162
4163         self.check_access_rule(cr, user, [id_new], 'create', context=context)
4164         self.create_workflow(cr, user, [id_new], context=context)
4165         return id_new
4166
4167     def _store_get_values(self, cr, uid, ids, fields, context):
4168         """Returns an ordered list of fields.function to call due to
4169            an update operation on ``fields`` of records with ``ids``,
4170            obtained by calling the 'store' triggers of these fields,
4171            as setup by their 'store' attribute.
4172
4173            :return: [(priority, model_name, [record_ids,], [function_fields,])]
4174         """
4175         if fields is None: fields = []
4176         stored_functions = self.pool._store_function.get(self._name, [])
4177
4178         # use indexed names for the details of the stored_functions:
4179         model_name_, func_field_to_compute_, target_ids_func_, trigger_fields_, priority_ = range(5)
4180
4181         # only keep store triggers that should be triggered for the ``fields``
4182         # being written to.
4183         triggers_to_compute = (
4184             f for f in stored_functions
4185             if not f[trigger_fields_] or set(fields).intersection(f[trigger_fields_])
4186         )
4187
4188         to_compute_map = {}
4189         target_id_results = {}
4190         for store_trigger in triggers_to_compute:
4191             target_func_id_ = id(store_trigger[target_ids_func_])
4192             if target_func_id_ not in target_id_results:
4193                 # use admin user for accessing objects having rules defined on store fields
4194                 target_id_results[target_func_id_] = [i for i in store_trigger[target_ids_func_](self, cr, SUPERUSER_ID, ids, context) if i]
4195             target_ids = target_id_results[target_func_id_]
4196
4197             # the compound key must consider the priority and model name
4198             key = (store_trigger[priority_], store_trigger[model_name_])
4199             for target_id in target_ids:
4200                 to_compute_map.setdefault(key, {}).setdefault(target_id,set()).add(tuple(store_trigger))
4201
4202         # Here to_compute_map looks like:
4203         # { (10, 'model_a') : { target_id1: [ (trigger_1_tuple, trigger_2_tuple) ], ... }
4204         #   (20, 'model_a') : { target_id2: [ (trigger_3_tuple, trigger_4_tuple) ], ... }
4205         #   (99, 'model_a') : { target_id1: [ (trigger_5_tuple, trigger_6_tuple) ], ... }
4206         # }
4207
4208         # Now we need to generate the batch function calls list
4209         # call_map =
4210         #   { (10, 'model_a') : [(10, 'model_a', [record_ids,], [function_fields,])] }
4211         call_map = {}
4212         for ((priority,model), id_map) in to_compute_map.iteritems():
4213             trigger_ids_maps = {}
4214             # function_ids_maps =
4215             #   { (function_1_tuple, function_2_tuple) : [target_id1, target_id2, ..] }
4216             for target_id, triggers in id_map.iteritems():
4217                 trigger_ids_maps.setdefault(tuple(triggers), []).append(target_id)
4218             for triggers, target_ids in trigger_ids_maps.iteritems():
4219                 call_map.setdefault((priority,model),[]).append((priority, model, target_ids,
4220                                                                  [t[func_field_to_compute_] for t in triggers]))
4221         result = []
4222         if call_map:
4223             result = reduce(operator.add, (call_map[k] for k in sorted(call_map)))
4224         return result
4225
4226     def _store_set_values(self, cr, uid, ids, fields, context):
4227         """Calls the fields.function's "implementation function" for all ``fields``, on records with ``ids`` (taking care of
4228            respecting ``multi`` attributes), and stores the resulting values in the database directly."""
4229         if not ids:
4230             return True
4231         field_flag = False
4232         field_dict = {}
4233         if self._log_access:
4234             cr.execute('select id,write_date from '+self._table+' where id IN %s', (tuple(ids),))
4235             res = cr.fetchall()
4236             for r in res:
4237                 if r[1]:
4238                     field_dict.setdefault(r[0], [])
4239                     res_date = time.strptime((r[1])[:19], '%Y-%m-%d %H:%M:%S')
4240                     write_date = datetime.datetime.fromtimestamp(time.mktime(res_date))
4241                     for i in self.pool._store_function.get(self._name, []):
4242                         if i[5]:
4243                             up_write_date = write_date + datetime.timedelta(hours=i[5])
4244                             if datetime.datetime.now() < up_write_date:
4245                                 if i[1] in fields:
4246                                     field_dict[r[0]].append(i[1])
4247                                     if not field_flag:
4248                                         field_flag = True
4249         todo = {}
4250         keys = []
4251         for f in fields:
4252             if self._columns[f]._multi not in keys:
4253                 keys.append(self._columns[f]._multi)
4254             todo.setdefault(self._columns[f]._multi, [])
4255             todo[self._columns[f]._multi].append(f)
4256         for key in keys:
4257             val = todo[key]
4258             if key:
4259                 # use admin user for accessing objects having rules defined on store fields
4260                 result = self._columns[val[0]].get(cr, self, ids, val, SUPERUSER_ID, context=context)
4261                 for id, value in result.items():
4262                     if field_flag:
4263                         for f in value.keys():
4264                             if f in field_dict[id]:
4265                                 value.pop(f)
4266                     upd0 = []
4267                     upd1 = []
4268                     for v in value:
4269                         if v not in val:
4270                             continue
4271                         if self._columns[v]._type == 'many2one':
4272                             try:
4273                                 value[v] = value[v][0]
4274                             except:
4275                                 pass
4276                         upd0.append('"'+v+'"='+self._columns[v]._symbol_set[0])
4277                         upd1.append(self._columns[v]._symbol_set[1](value[v]))
4278                     upd1.append(id)
4279                     if upd0 and upd1:
4280                         cr.execute('update "' + self._table + '" set ' + \
4281                             ','.join(upd0) + ' where id = %s', upd1)
4282
4283             else:
4284                 for f in val:
4285                     # use admin user for accessing objects having rules defined on store fields
4286                     result = self._columns[f].get(cr, self, ids, f, SUPERUSER_ID, context=context)
4287                     for r in result.keys():
4288                         if field_flag:
4289                             if r in field_dict.keys():
4290                                 if f in field_dict[r]:
4291                                     result.pop(r)
4292                     for id, value in result.items():
4293                         if self._columns[f]._type == 'many2one':
4294                             try:
4295                                 value = value[0]
4296                             except:
4297                                 pass
4298                         cr.execute('update "' + self._table + '" set ' + \
4299                             '"'+f+'"='+self._columns[f]._symbol_set[0] + ' where id = %s', (self._columns[f]._symbol_set[1](value), id))
4300
4301         # invalidate and mark new-style fields to recompute
4302         self.browse(cr, uid, ids, context).modified(fields)
4303
4304         return True
4305
4306     # TODO: ameliorer avec NULL
4307     def _where_calc(self, cr, user, domain, active_test=True, context=None):
4308         """Computes the WHERE clause needed to implement an OpenERP domain.
4309         :param domain: the domain to compute
4310         :type domain: list
4311         :param active_test: whether the default filtering of records with ``active``
4312                             field set to ``False`` should be applied.
4313         :return: the query expressing the given domain as provided in domain
4314         :rtype: osv.query.Query
4315         """
4316         if not context:
4317             context = {}
4318         domain = domain[:]
4319         # if the object has a field named 'active', filter out all inactive
4320         # records unless they were explicitely asked for
4321         if 'active' in self._all_columns and (active_test and context.get('active_test', True)):
4322             if domain:
4323                 # the item[0] trick below works for domain items and '&'/'|'/'!'
4324                 # operators too
4325                 if not any(item[0] == 'active' for item in domain):
4326                     domain.insert(0, ('active', '=', 1))
4327             else:
4328                 domain = [('active', '=', 1)]
4329
4330         if domain:
4331             e = expression.expression(cr, user, domain, self, context)
4332             tables = e.get_tables()
4333             where_clause, where_params = e.to_sql()
4334             where_clause = where_clause and [where_clause] or []
4335         else:
4336             where_clause, where_params, tables = [], [], ['"%s"' % self._table]
4337
4338         return Query(tables, where_clause, where_params)
4339
4340     def _check_qorder(self, word):
4341         if not regex_order.match(word):
4342             raise except_orm(_('AccessError'), _('Invalid "order" specified. A valid "order" specification is a comma-separated list of valid field names (optionally followed by asc/desc for the direction)'))
4343         return True
4344
4345     def _apply_ir_rules(self, cr, uid, query, mode='read', context=None):
4346         """Add what's missing in ``query`` to implement all appropriate ir.rules
4347           (using the ``model_name``'s rules or the current model's rules if ``model_name`` is None)
4348
4349            :param query: the current query object
4350         """
4351         if uid == SUPERUSER_ID:
4352             return
4353
4354         def apply_rule(added_clause, added_params, added_tables, parent_model=None):
4355             """ :param parent_model: name of the parent model, if the added
4356                     clause comes from a parent model
4357             """
4358             if added_clause:
4359                 if parent_model:
4360                     # as inherited rules are being applied, we need to add the missing JOIN
4361                     # to reach the parent table (if it was not JOINed yet in the query)
4362                     parent_alias = self._inherits_join_add(self, parent_model, query)
4363                     # inherited rules are applied on the external table -> need to get the alias and replace
4364                     parent_table = self.pool[parent_model]._table
4365                     added_clause = [clause.replace('"%s"' % parent_table, '"%s"' % parent_alias) for clause in added_clause]
4366                     # change references to parent_table to parent_alias, because we now use the alias to refer to the table
4367                     new_tables = []
4368                     for table in added_tables:
4369                         # table is just a table name -> switch to the full alias
4370                         if table == '"%s"' % parent_table:
4371                             new_tables.append('"%s" as "%s"' % (parent_table, parent_alias))
4372                         # table is already a full statement -> replace reference to the table to its alias, is correct with the way aliases are generated
4373                         else:
4374                             new_tables.append(table.replace('"%s"' % parent_table, '"%s"' % parent_alias))
4375                     added_tables = new_tables
4376                 query.where_clause += added_clause
4377                 query.where_clause_params += added_params
4378                 for table in added_tables:
4379                     if table not in query.tables:
4380                         query.tables.append(table)
4381                 return True
4382             return False
4383
4384         # apply main rules on the object
4385         rule_obj = self.pool.get('ir.rule')
4386         rule_where_clause, rule_where_clause_params, rule_tables = rule_obj.domain_get(cr, uid, self._name, mode, context=context)
4387         apply_rule(rule_where_clause, rule_where_clause_params, rule_tables)
4388
4389         # apply ir.rules from the parents (through _inherits)
4390         for inherited_model in self._inherits:
4391             rule_where_clause, rule_where_clause_params, rule_tables = rule_obj.domain_get(cr, uid, inherited_model, mode, context=context)
4392             apply_rule(rule_where_clause, rule_where_clause_params, rule_tables,
4393                         parent_model=inherited_model)
4394
4395     def _generate_m2o_order_by(self, order_field, query):
4396         """
4397         Add possibly missing JOIN to ``query`` and generate the ORDER BY clause for m2o fields,
4398         either native m2o fields or function/related fields that are stored, including
4399         intermediate JOINs for inheritance if required.
4400
4401         :return: the qualified field name to use in an ORDER BY clause to sort by ``order_field``
4402         """
4403         if order_field not in self._columns and order_field in self._inherit_fields:
4404             # also add missing joins for reaching the table containing the m2o field
4405             qualified_field = self._inherits_join_calc(order_field, query)
4406             order_field_column = self._inherit_fields[order_field][2]
4407         else:
4408             qualified_field = '"%s"."%s"' % (self._table, order_field)
4409             order_field_column = self._columns[order_field]
4410
4411         assert order_field_column._type == 'many2one', 'Invalid field passed to _generate_m2o_order_by()'
4412         if not order_field_column._classic_write and not getattr(order_field_column, 'store', False):
4413             _logger.debug("Many2one function/related fields must be stored " \
4414                 "to be used as ordering fields! Ignoring sorting for %s.%s",
4415                 self._name, order_field)
4416             return
4417
4418         # figure out the applicable order_by for the m2o
4419         dest_model = self.pool[order_field_column._obj]
4420         m2o_order = dest_model._order
4421         if not regex_order.match(m2o_order):
4422             # _order is complex, can't use it here, so we default to _rec_name
4423             m2o_order = dest_model._rec_name
4424         else:
4425             # extract the field names, to be able to qualify them and add desc/asc
4426             m2o_order_list = []
4427             for order_part in m2o_order.split(","):
4428                 m2o_order_list.append(order_part.strip().split(" ", 1)[0].strip())
4429             m2o_order = m2o_order_list
4430
4431         # Join the dest m2o table if it's not joined yet. We use [LEFT] OUTER join here
4432         # as we don't want to exclude results that have NULL values for the m2o
4433         src_table, src_field = qualified_field.replace('"', '').split('.', 1)
4434         dst_alias, dst_alias_statement = query.add_join((src_table, dest_model._table, src_field, 'id', src_field), implicit=False, outer=True)
4435         qualify = lambda field: '"%s"."%s"' % (dst_alias, field)
4436         return map(qualify, m2o_order) if isinstance(m2o_order, list) else qualify(m2o_order)
4437
4438     def _generate_order_by(self, order_spec, query):
4439         """
4440         Attempt to consruct an appropriate ORDER BY clause based on order_spec, which must be
4441         a comma-separated list of valid field names, optionally followed by an ASC or DESC direction.
4442
4443         :raise" except_orm in case order_spec is malformed
4444         """
4445         order_by_clause = ''
4446         order_spec = order_spec or self._order
4447         if order_spec:
4448             order_by_elements = []
4449             self._check_qorder(order_spec)
4450             for order_part in order_spec.split(','):
4451                 order_split = order_part.strip().split(' ')
4452                 order_field = order_split[0].strip()
4453                 order_direction = order_split[1].strip() if len(order_split) == 2 else ''
4454                 order_column = None
4455                 inner_clause = None
4456                 if order_field == 'id':
4457                     order_by_elements.append('"%s"."%s" %s' % (self._table, order_field, order_direction))
4458                 elif order_field in self._columns:
4459                     order_column = self._columns[order_field]
4460                     if order_column._classic_read:
4461                         inner_clause = '"%s"."%s"' % (self._table, order_field)
4462                     elif order_column._type == 'many2one':
4463                         inner_clause = self._generate_m2o_order_by(order_field, query)
4464                     else:
4465                         continue  # ignore non-readable or "non-joinable" fields
4466                 elif order_field in self._inherit_fields:
4467                     parent_obj = self.pool[self._inherit_fields[order_field][3]]
4468                     order_column = parent_obj._columns[order_field]
4469                     if order_column._classic_read:
4470                         inner_clause = self._inherits_join_calc(order_field, query)
4471                     elif order_column._type == 'many2one':
4472                         inner_clause = self._generate_m2o_order_by(order_field, query)
4473                     else:
4474                         continue  # ignore non-readable or "non-joinable" fields
4475                 else:
4476                     raise ValueError( _("Sorting field %s not found on model %s") %( order_field, self._name))
4477                 if order_column and order_column._type == 'boolean':
4478                     inner_clause = "COALESCE(%s, false)" % inner_clause
4479                 if inner_clause:
4480                     if isinstance(inner_clause, list):
4481                         for clause in inner_clause:
4482                             order_by_elements.append("%s %s" % (clause, order_direction))
4483                     else:
4484                         order_by_elements.append("%s %s" % (inner_clause, order_direction))
4485             if order_by_elements:
4486                 order_by_clause = ",".join(order_by_elements)
4487
4488         return order_by_clause and (' ORDER BY %s ' % order_by_clause) or ''
4489
4490     def _search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False, access_rights_uid=None):
4491         """
4492         Private implementation of search() method, allowing specifying the uid to use for the access right check.
4493         This is useful for example when filling in the selection list for a drop-down and avoiding access rights errors,
4494         by specifying ``access_rights_uid=1`` to bypass access rights check, but not ir.rules!
4495         This is ok at the security level because this method is private and not callable through XML-RPC.
4496
4497         :param access_rights_uid: optional user ID to use when checking access rights
4498                                   (not for ir.rules, this is only for ir.model.access)
4499         """
4500         if context is None:
4501             context = {}
4502         self.check_access_rights(cr, access_rights_uid or user, 'read')
4503
4504         # For transient models, restrict acces to the current user, except for the super-user
4505         if self.is_transient() and self._log_access and user != SUPERUSER_ID:
4506             args = expression.AND(([('create_uid', '=', user)], args or []))
4507
4508         query = self._where_calc(cr, user, args, context=context)
4509         self._apply_ir_rules(cr, user, query, 'read', context=context)
4510         order_by = self._generate_order_by(order, query)
4511         from_clause, where_clause, where_clause_params = query.get_sql()
4512
4513         where_str = where_clause and (" WHERE %s" % where_clause) or ''
4514
4515         if count:
4516             # Ignore order, limit and offset when just counting, they don't make sense and could
4517             # hurt performance
4518             query_str = 'SELECT count(1) FROM ' + from_clause + where_str
4519             cr.execute(query_str, where_clause_params)
4520             res = cr.fetchone()
4521             return res[0]
4522
4523         limit_str = limit and ' limit %d' % limit or ''
4524         offset_str = offset and ' offset %d' % offset or ''
4525         query_str = 'SELECT "%s".id FROM ' % self._table + from_clause + where_str + order_by + limit_str + offset_str
4526         cr.execute(query_str, where_clause_params)
4527         res = cr.fetchall()
4528
4529         # TDE note: with auto_join, we could have several lines about the same result
4530         # i.e. a lead with several unread messages; we uniquify the result using
4531         # a fast way to do it while preserving order (http://www.peterbe.com/plog/uniqifiers-benchmark)
4532         def _uniquify_list(seq):
4533             seen = set()
4534             return [x for x in seq if x not in seen and not seen.add(x)]
4535
4536         return _uniquify_list([x[0] for x in res])
4537
4538     # returns the different values ever entered for one field
4539     # this is used, for example, in the client when the user hits enter on
4540     # a char field
4541     def distinct_field_get(self, cr, uid, field, value, args=None, offset=0, limit=None):
4542         if not args:
4543             args = []
4544         if field in self._inherit_fields:
4545             return self.pool[self._inherit_fields[field][0]].distinct_field_get(cr, uid, field, value, args, offset, limit)
4546         else:
4547             return self._columns[field].search(cr, self, args, field, value, offset, limit, uid)
4548
4549     def copy_data(self, cr, uid, id, default=None, context=None):
4550         """
4551         Copy given record's data with all its fields values
4552
4553         :param cr: database cursor
4554         :param uid: current user id
4555         :param id: id of the record to copy
4556         :param default: field values to override in the original values of the copied record
4557         :type default: dictionary
4558         :param context: context arguments, like lang, time zone
4559         :type context: dictionary
4560         :return: dictionary containing all the field values
4561         """
4562
4563         if context is None:
4564             context = {}
4565
4566         # avoid recursion through already copied records in case of circular relationship
4567         seen_map = context.setdefault('__copy_data_seen', {})
4568         if id in seen_map.setdefault(self._name, []):
4569             return
4570         seen_map[self._name].append(id)
4571
4572         if default is None:
4573             default = {}
4574         if 'state' not in default:
4575             if 'state' in self._defaults:
4576                 if callable(self._defaults['state']):
4577                     default['state'] = self._defaults['state'](self, cr, uid, context)
4578                 else:
4579                     default['state'] = self._defaults['state']
4580
4581         # build a black list of fields that should not be copied
4582         blacklist = set(MAGIC_COLUMNS + ['parent_left', 'parent_right'])
4583         def blacklist_given_fields(obj):
4584             # blacklist the fields that are given by inheritance
4585             for other, field_to_other in obj._inherits.items():
4586                 blacklist.add(field_to_other)
4587                 if field_to_other in default:
4588                     # all the fields of 'other' are given by the record: default[field_to_other],
4589                     # except the ones redefined in self
4590                     blacklist.update(set(self.pool[other]._all_columns) - set(self._columns))
4591                 else:
4592                     blacklist_given_fields(self.pool[other])
4593             # blacklist deprecated fields
4594             for name, field in obj._columns.items():
4595                 if field.deprecated:
4596                     blacklist.add(name)
4597
4598         blacklist_given_fields(self)
4599
4600
4601         fields_to_copy = dict((f,fi) for f, fi in self._all_columns.iteritems()
4602                                      if fi.column.copy
4603                                      if f not in default
4604                                      if f not in blacklist)
4605
4606         data = self.read(cr, uid, [id], fields_to_copy.keys(), context=context)
4607         if data:
4608             data = data[0]
4609         else:
4610             raise IndexError( _("Record #%d of %s not found, cannot copy!") %( id, self._name))
4611
4612         res = dict(default)
4613         for f, colinfo in fields_to_copy.iteritems():
4614             field = colinfo.column
4615             if field._type == 'many2one':
4616                 res[f] = data[f] and data[f][0]
4617             elif field._type == 'one2many':
4618                 other = self.pool[field._obj]
4619                 # duplicate following the order of the ids because we'll rely on
4620                 # it later for copying translations in copy_translation()!
4621                 lines = [other.copy_data(cr, uid, line_id, context=context) for line_id in sorted(data[f])]
4622                 # the lines are duplicated using the wrong (old) parent, but then
4623                 # are reassigned to the correct one thanks to the (0, 0, ...)
4624                 res[f] = [(0, 0, line) for line in lines if line]
4625             elif field._type == 'many2many':
4626                 res[f] = [(6, 0, data[f])]
4627             else:
4628                 res[f] = data[f]
4629
4630         return res
4631
4632     def copy_translations(self, cr, uid, old_id, new_id, context=None):
4633         if context is None:
4634             context = {}
4635
4636         # avoid recursion through already copied records in case of circular relationship
4637         seen_map = context.setdefault('__copy_translations_seen',{})
4638         if old_id in seen_map.setdefault(self._name,[]):
4639             return
4640         seen_map[self._name].append(old_id)
4641
4642         trans_obj = self.pool.get('ir.translation')
4643         # TODO it seems fields_get can be replaced by _all_columns (no need for translation)
4644         fields = self.fields_get(cr, uid, context=context)
4645
4646         for field_name, field_def in fields.items():
4647             # removing the lang to compare untranslated values
4648             context_wo_lang = dict(context, lang=None)
4649             old_record, new_record = self.browse(cr, uid, [old_id, new_id], context=context_wo_lang)
4650             # we must recursively copy the translations for o2o and o2m
4651             if field_def['type'] == 'one2many':
4652                 target_obj = self.pool[field_def['relation']]
4653                 # here we rely on the order of the ids to match the translations
4654                 # as foreseen in copy_data()
4655                 old_children = sorted(r.id for r in old_record[field_name])
4656                 new_children = sorted(r.id for r in new_record[field_name])
4657                 for (old_child, new_child) in zip(old_children, new_children):
4658                     target_obj.copy_translations(cr, uid, old_child, new_child, context=context)
4659             # and for translatable fields we keep them for copy
4660             elif field_def.get('translate'):
4661                 if field_name in self._columns:
4662                     trans_name = self._name + "," + field_name
4663                     target_id = new_id
4664                     source_id = old_id
4665                 elif field_name in self._inherit_fields:
4666                     trans_name = self._inherit_fields[field_name][0] + "," + field_name
4667                     # get the id of the parent record to set the translation
4668                     inherit_field_name = self._inherit_fields[field_name][1]
4669                     target_id = new_record[inherit_field_name].id
4670                     source_id = old_record[inherit_field_name].id
4671                 else:
4672                     continue
4673
4674                 trans_ids = trans_obj.search(cr, uid, [
4675                         ('name', '=', trans_name),
4676                         ('res_id', '=', source_id)
4677                 ])
4678                 user_lang = context.get('lang')
4679                 for record in trans_obj.read(cr, uid, trans_ids, context=context):
4680                     del record['id']
4681                     # remove source to avoid triggering _set_src
4682                     del record['source']
4683                     record.update({'res_id': target_id})
4684                     if user_lang and user_lang == record['lang']:
4685                         # 'source' to force the call to _set_src
4686                         # 'value' needed if value is changed in copy(), want to see the new_value
4687                         record['source'] = old_record[field_name]
4688                         record['value'] = new_record[field_name]
4689                     trans_obj.create(cr, uid, record, context=context)
4690
4691     @api.returns('self', lambda value: value.id)
4692     def copy(self, cr, uid, id, default=None, context=None):
4693         """ copy(default=None)
4694
4695         Duplicate record with given id updating it with default values
4696
4697         :param dict default: dictionary of field values to override in the
4698                original values of the copied record, e.g: ``{'field_name': overriden_value, ...}``
4699         :returns: new record
4700
4701         """
4702         if context is None:
4703             context = {}
4704         context = context.copy()
4705         data = self.copy_data(cr, uid, id, default, context)
4706         new_id = self.create(cr, uid, data, context)
4707         self.copy_translations(cr, uid, id, new_id, context)
4708         return new_id
4709
4710     @api.multi
4711     @api.returns('self')
4712     def exists(self):
4713         """  exists() -> records
4714
4715         Returns the subset of records in `self` that exist, and marks deleted
4716         records as such in cache. It can be used as a test on records::
4717
4718             if record.exists():
4719                 ...
4720
4721         By convention, new records are returned as existing.
4722         """
4723         ids = filter(None, self._ids)           # ids to check in database
4724         if not ids:
4725             return self
4726         query = """SELECT id FROM "%s" WHERE id IN %%s""" % self._table
4727         self._cr.execute(query, (ids,))
4728         ids = ([r[0] for r in self._cr.fetchall()] +    # ids in database
4729                [id for id in self._ids if not id])      # new ids
4730         existing = self.browse(ids)
4731         if len(existing) < len(self):
4732             # mark missing records in cache with a failed value
4733             exc = MissingError(_("Record does not exist or has been deleted."))
4734             (self - existing)._cache.update(FailedValue(exc))
4735         return existing
4736
4737     def check_recursion(self, cr, uid, ids, context=None, parent=None):
4738         _logger.warning("You are using deprecated %s.check_recursion(). Please use the '_check_recursion()' instead!" % \
4739                         self._name)
4740         assert parent is None or parent in self._columns or parent in self._inherit_fields,\
4741                     "The 'parent' parameter passed to check_recursion() must be None or a valid field name"
4742         return self._check_recursion(cr, uid, ids, context, parent)
4743
4744     def _check_recursion(self, cr, uid, ids, context=None, parent=None):
4745         """
4746         Verifies that there is no loop in a hierarchical structure of records,
4747         by following the parent relationship using the **parent** field until a loop
4748         is detected or until a top-level record is found.
4749
4750         :param cr: database cursor
4751         :param uid: current user id
4752         :param ids: list of ids of records to check
4753         :param parent: optional parent field name (default: ``self._parent_name = parent_id``)
4754         :return: **True** if the operation can proceed safely, or **False** if an infinite loop is detected.
4755         """
4756         if not parent:
4757             parent = self._parent_name
4758
4759         # must ignore 'active' flag, ir.rules, etc. => direct SQL query
4760         query = 'SELECT "%s" FROM "%s" WHERE id = %%s' % (parent, self._table)
4761         for id in ids:
4762             current_id = id
4763             while current_id is not None:
4764                 cr.execute(query, (current_id,))
4765                 result = cr.fetchone()
4766                 current_id = result[0] if result else None
4767                 if current_id == id:
4768                     return False
4769         return True
4770
4771     def _check_m2m_recursion(self, cr, uid, ids, field_name):
4772         """
4773         Verifies that there is no loop in a hierarchical structure of records,
4774         by following the parent relationship using the **parent** field until a loop
4775         is detected or until a top-level record is found.
4776
4777         :param cr: database cursor
4778         :param uid: current user id
4779         :param ids: list of ids of records to check
4780         :param field_name: field to check
4781         :return: **True** if the operation can proceed safely, or **False** if an infinite loop is detected.
4782         """
4783
4784         field = self._all_columns.get(field_name)
4785         field = field.column if field else None
4786         if not field or field._type != 'many2many' or field._obj != self._name:
4787             # field must be a many2many on itself
4788             raise ValueError('invalid field_name: %r' % (field_name,))
4789
4790         query = 'SELECT distinct "%s" FROM "%s" WHERE "%s" IN %%s' % (field._id2, field._rel, field._id1)
4791         ids_parent = ids[:]
4792         while ids_parent:
4793             ids_parent2 = []
4794             for i in range(0, len(ids_parent), cr.IN_MAX):
4795                 j = i + cr.IN_MAX
4796                 sub_ids_parent = ids_parent[i:j]
4797                 cr.execute(query, (tuple(sub_ids_parent),))
4798                 ids_parent2.extend(filter(None, map(lambda x: x[0], cr.fetchall())))
4799             ids_parent = ids_parent2
4800             for i in ids_parent:
4801                 if i in ids:
4802                     return False
4803         return True
4804
4805     def _get_external_ids(self, cr, uid, ids, *args, **kwargs):
4806         """Retrieve the External ID(s) of any database record.
4807
4808         **Synopsis**: ``_get_xml_ids(cr, uid, ids) -> { 'id': ['module.xml_id'] }``
4809
4810         :return: map of ids to the list of their fully qualified External IDs
4811                  in the form ``module.key``, or an empty list when there's no External
4812                  ID for a record, e.g.::
4813
4814                      { 'id': ['module.ext_id', 'module.ext_id_bis'],
4815                        'id2': [] }
4816         """
4817         ir_model_data = self.pool.get('ir.model.data')
4818         data_ids = ir_model_data.search(cr, uid, [('model', '=', self._name), ('res_id', 'in', ids)])
4819         data_results = ir_model_data.read(cr, uid, data_ids, ['module', 'name', 'res_id'])
4820         result = {}
4821         for id in ids:
4822             # can't use dict.fromkeys() as the list would be shared!
4823             result[id] = []
4824         for record in data_results:
4825             result[record['res_id']].append('%(module)s.%(name)s' % record)
4826         return result
4827
4828     def get_external_id(self, cr, uid, ids, *args, **kwargs):
4829         """Retrieve the External ID of any database record, if there
4830         is one. This method works as a possible implementation
4831         for a function field, to be able to add it to any
4832         model object easily, referencing it as ``Model.get_external_id``.
4833
4834         When multiple External IDs exist for a record, only one
4835         of them is returned (randomly).
4836
4837         :return: map of ids to their fully qualified XML ID,
4838                  defaulting to an empty string when there's none
4839                  (to be usable as a function field),
4840                  e.g.::
4841
4842                      { 'id': 'module.ext_id',
4843                        'id2': '' }
4844         """
4845         results = self._get_xml_ids(cr, uid, ids)
4846         for k, v in results.iteritems():
4847             if results[k]:
4848                 results[k] = v[0]
4849             else:
4850                 results[k] = ''
4851         return results
4852
4853     # backwards compatibility
4854     get_xml_id = get_external_id
4855     _get_xml_ids = _get_external_ids
4856
4857     def print_report(self, cr, uid, ids, name, data, context=None):
4858         """
4859         Render the report `name` for the given IDs. The report must be defined
4860         for this model, not another.
4861         """
4862         report = self.pool['ir.actions.report.xml']._lookup_report(cr, name)
4863         assert self._name == report.table
4864         return report.create(cr, uid, ids, data, context)
4865
4866     # Transience
4867     @classmethod
4868     def is_transient(cls):
4869         """ Return whether the model is transient.
4870
4871         See :class:`TransientModel`.
4872
4873         """
4874         return cls._transient
4875
4876     def _transient_clean_rows_older_than(self, cr, seconds):
4877         assert self._transient, "Model %s is not transient, it cannot be vacuumed!" % self._name
4878         # Never delete rows used in last 5 minutes
4879         seconds = max(seconds, 300)
4880         query = ("SELECT id FROM " + self._table + " WHERE"
4881             " COALESCE(write_date, create_date, (now() at time zone 'UTC'))::timestamp"
4882             " < ((now() at time zone 'UTC') - interval %s)")
4883         cr.execute(query, ("%s seconds" % seconds,))
4884         ids = [x[0] for x in cr.fetchall()]
4885         self.unlink(cr, SUPERUSER_ID, ids)
4886
4887     def _transient_clean_old_rows(self, cr, max_count):
4888         # Check how many rows we have in the table
4889         cr.execute("SELECT count(*) AS row_count FROM " + self._table)
4890         res = cr.fetchall()
4891         if res[0][0] <= max_count:
4892             return  # max not reached, nothing to do
4893         self._transient_clean_rows_older_than(cr, 300)
4894
4895     def _transient_vacuum(self, cr, uid, force=False):
4896         """Clean the transient records.
4897
4898         This unlinks old records from the transient model tables whenever the
4899         "_transient_max_count" or "_max_age" conditions (if any) are reached.
4900         Actual cleaning will happen only once every "_transient_check_time" calls.
4901         This means this method can be called frequently called (e.g. whenever
4902         a new record is created).
4903         Example with both max_hours and max_count active:
4904         Suppose max_hours = 0.2 (e.g. 12 minutes), max_count = 20, there are 55 rows in the
4905         table, 10 created/changed in the last 5 minutes, an additional 12 created/changed between
4906         5 and 10 minutes ago, the rest created/changed more then 12 minutes ago.
4907         - age based vacuum will leave the 22 rows created/changed in the last 12 minutes
4908         - count based vacuum will wipe out another 12 rows. Not just 2, otherwise each addition
4909           would immediately cause the maximum to be reached again.
4910         - the 10 rows that have been created/changed the last 5 minutes will NOT be deleted
4911         """
4912         assert self._transient, "Model %s is not transient, it cannot be vacuumed!" % self._name
4913         _transient_check_time = 20          # arbitrary limit on vacuum executions
4914         self._transient_check_count += 1
4915         if not force and (self._transient_check_count < _transient_check_time):
4916             return True  # no vacuum cleaning this time
4917         self._transient_check_count = 0
4918
4919         # Age-based expiration
4920         if self._transient_max_hours:
4921             self._transient_clean_rows_older_than(cr, self._transient_max_hours * 60 * 60)
4922
4923         # Count-based expiration
4924         if self._transient_max_count:
4925             self._transient_clean_old_rows(cr, self._transient_max_count)
4926
4927         return True
4928
4929     def resolve_2many_commands(self, cr, uid, field_name, commands, fields=None, context=None):
4930         """ Serializes one2many and many2many commands into record dictionaries
4931             (as if all the records came from the database via a read()).  This
4932             method is aimed at onchange methods on one2many and many2many fields.
4933
4934             Because commands might be creation commands, not all record dicts
4935             will contain an ``id`` field.  Commands matching an existing record
4936             will have an ``id``.
4937
4938             :param field_name: name of the one2many or many2many field matching the commands
4939             :type field_name: str
4940             :param commands: one2many or many2many commands to execute on ``field_name``
4941             :type commands: list((int|False, int|False, dict|False))
4942             :param fields: list of fields to read from the database, when applicable
4943             :type fields: list(str)
4944             :returns: records in a shape similar to that returned by ``read()``
4945                 (except records may be missing the ``id`` field if they don't exist in db)
4946             :rtype: list(dict)
4947         """
4948         result = []             # result (list of dict)
4949         record_ids = []         # ids of records to read
4950         updates = {}            # {id: dict} of updates on particular records
4951
4952         for command in commands or []:
4953             if not isinstance(command, (list, tuple)):
4954                 record_ids.append(command)
4955             elif command[0] == 0:
4956                 result.append(command[2])
4957             elif command[0] == 1:
4958                 record_ids.append(command[1])
4959                 updates.setdefault(command[1], {}).update(command[2])
4960             elif command[0] in (2, 3):
4961                 record_ids = [id for id in record_ids if id != command[1]]
4962             elif command[0] == 4:
4963                 record_ids.append(command[1])
4964             elif command[0] == 5:
4965                 result, record_ids = [], []
4966             elif command[0] == 6:
4967                 result, record_ids = [], list(command[2])
4968
4969         # read the records and apply the updates
4970         other_model = self.pool[self._all_columns[field_name].column._obj]
4971         for record in other_model.read(cr, uid, record_ids, fields=fields, context=context):
4972             record.update(updates.get(record['id'], {}))
4973             result.append(record)
4974
4975         return result
4976
4977     # for backward compatibility
4978     resolve_o2m_commands_to_record_dicts = resolve_2many_commands
4979
4980     def search_read(self, cr, uid, domain=None, fields=None, offset=0, limit=None, order=None, context=None):
4981         """
4982         Performs a ``search()`` followed by a ``read()``.
4983
4984         :param cr: database cursor
4985         :param user: current user id
4986         :param domain: Search domain, see ``args`` parameter in ``search()``. Defaults to an empty domain that will match all records.
4987         :param fields: List of fields to read, see ``fields`` parameter in ``read()``. Defaults to all fields.
4988         :param offset: Number of records to skip, see ``offset`` parameter in ``search()``. Defaults to 0.
4989         :param limit: Maximum number of records to return, see ``limit`` parameter in ``search()``. Defaults to no limit.
4990         :param order: Columns to sort result, see ``order`` parameter in ``search()``. Defaults to no sort.
4991         :param context: context arguments.
4992         :return: List of dictionaries containing the asked fields.
4993         :rtype: List of dictionaries.
4994
4995         """
4996         record_ids = self.search(cr, uid, domain or [], offset=offset, limit=limit, order=order, context=context)
4997         if not record_ids:
4998             return []
4999
5000         if fields and fields == ['id']:
5001             # shortcut read if we only want the ids
5002             return [{'id': id} for id in record_ids]
5003
5004         # read() ignores active_test, but it would forward it to any downstream search call
5005         # (e.g. for x2m or function fields), and this is not the desired behavior, the flag
5006         # was presumably only meant for the main search().
5007         # TODO: Move this to read() directly?                                                                                                
5008         read_ctx = dict(context or {})                                                                                                       
5009         read_ctx.pop('active_test', None)                                                                                                    
5010                                                                                                                                              
5011         result = self.read(cr, uid, record_ids, fields, context=read_ctx) 
5012         if len(result) <= 1:
5013             return result
5014
5015         # reorder read
5016         index = dict((r['id'], r) for r in result)
5017         return [index[x] for x in record_ids if x in index]
5018
5019     def _register_hook(self, cr):
5020         """ stuff to do right after the registry is built """
5021         pass
5022
5023     @classmethod
5024     def _patch_method(cls, name, method):
5025         """ Monkey-patch a method for all instances of this model. This replaces
5026             the method called `name` by `method` in the given class.
5027             The original method is then accessible via ``method.origin``, and it
5028             can be restored with :meth:`~._revert_method`.
5029
5030             Example::
5031
5032                 @api.multi
5033                 def do_write(self, values):
5034                     # do stuff, and call the original method
5035                     return do_write.origin(self, values)
5036
5037                 # patch method write of model
5038                 model._patch_method('write', do_write)
5039
5040                 # this will call do_write
5041                 records = model.search([...])
5042                 records.write(...)
5043
5044                 # restore the original method
5045                 model._revert_method('write')
5046         """
5047         origin = getattr(cls, name)
5048         method.origin = origin
5049         # propagate decorators from origin to method, and apply api decorator
5050         wrapped = api.guess(api.propagate(origin, method))
5051         wrapped.origin = origin
5052         setattr(cls, name, wrapped)
5053
5054     @classmethod
5055     def _revert_method(cls, name):
5056         """ Revert the original method called `name` in the given class.
5057             See :meth:`~._patch_method`.
5058         """
5059         method = getattr(cls, name)
5060         setattr(cls, name, method.origin)
5061
5062     #
5063     # Instance creation
5064     #
5065     # An instance represents an ordered collection of records in a given
5066     # execution environment. The instance object refers to the environment, and
5067     # the records themselves are represented by their cache dictionary. The 'id'
5068     # of each record is found in its corresponding cache dictionary.
5069     #
5070     # This design has the following advantages:
5071     #  - cache access is direct and thus fast;
5072     #  - one can consider records without an 'id' (see new records);
5073     #  - the global cache is only an index to "resolve" a record 'id'.
5074     #
5075
5076     @classmethod
5077     def _browse(cls, env, ids):
5078         """ Create an instance attached to `env`; `ids` is a tuple of record
5079             ids.
5080         """
5081         records = object.__new__(cls)
5082         records.env = env
5083         records._ids = ids
5084         env.prefetch[cls._name].update(ids)
5085         return records
5086
5087     @api.v7
5088     def browse(self, cr, uid, arg=None, context=None):
5089         ids = _normalize_ids(arg)
5090         #assert all(isinstance(id, IdType) for id in ids), "Browsing invalid ids: %s" % ids
5091         return self._browse(Environment(cr, uid, context or {}), ids)
5092
5093     @api.v8
5094     def browse(self, arg=None):
5095         """ browse([ids]) -> records
5096
5097         Returns a recordset for the ids provided as parameter in the current
5098         environment.
5099
5100         Can take no ids, a single id or a sequence of ids.
5101         """
5102         ids = _normalize_ids(arg)
5103         #assert all(isinstance(id, IdType) for id in ids), "Browsing invalid ids: %s" % ids
5104         return self._browse(self.env, ids)
5105
5106     #
5107     # Internal properties, for manipulating the instance's implementation
5108     #
5109
5110     @property
5111     def ids(self):
5112         """ List of actual record ids in this recordset (ignores placeholder
5113         ids for records to create)
5114         """
5115         return filter(None, list(self._ids))
5116
5117     # backward-compatibility with former browse records
5118     _cr = property(lambda self: self.env.cr)
5119     _uid = property(lambda self: self.env.uid)
5120     _context = property(lambda self: self.env.context)
5121
5122     #
5123     # Conversion methods
5124     #
5125
5126     def ensure_one(self):
5127         """ Verifies that the current recorset holds a single record. Raises
5128         an exception otherwise.
5129         """
5130         if len(self) == 1:
5131             return self
5132         raise except_orm("ValueError", "Expected singleton: %s" % self)
5133
5134     def with_env(self, env):
5135         """ Returns a new version of this recordset attached to the provided
5136         environment
5137
5138         :type env: :class:`~openerp.api.Environment`
5139         """
5140         return self._browse(env, self._ids)
5141
5142     def sudo(self, user=SUPERUSER_ID):
5143         """ sudo([user=SUPERUSER])
5144
5145         Returns a new version of this recordset attached to the provided
5146         user.
5147         """
5148         return self.with_env(self.env(user=user))
5149
5150     def with_context(self, *args, **kwargs):
5151         """ with_context([context][, **overrides]) -> records
5152
5153         Returns a new version of this recordset attached to an extended
5154         context.
5155
5156         The extended context is either the provided ``context`` in which
5157         ``overrides`` are merged or the *current* context in which
5158         ``overrides`` are merged e.g.::
5159
5160             # current context is {'key1': True}
5161             r2 = records.with_context({}, key2=True)
5162             # -> r2._context is {'key2': True}
5163             r2 = records.with_context(key2=True)
5164             # -> r2._context is {'key1': True, 'key2': True}
5165         """
5166         context = dict(args[0] if args else self._context, **kwargs)
5167         return self.with_env(self.env(context=context))
5168
5169     def _convert_to_cache(self, values, update=False, validate=True):
5170         """ Convert the `values` dictionary into cached values.
5171
5172             :param update: whether the conversion is made for updating `self`;
5173                 this is necessary for interpreting the commands of *2many fields
5174             :param validate: whether values must be checked
5175         """
5176         fields = self._fields
5177         target = self if update else self.browse()
5178         return {
5179             name: fields[name].convert_to_cache(value, target, validate=validate)
5180             for name, value in values.iteritems()
5181             if name in fields
5182         }
5183
5184     def _convert_to_write(self, values):
5185         """ Convert the `values` dictionary into the format of :meth:`write`. """
5186         fields = self._fields
5187         result = {}
5188         for name, value in values.iteritems():
5189             if name in fields:
5190                 value = fields[name].convert_to_write(value)
5191                 if not isinstance(value, NewId):
5192                     result[name] = value
5193         return result
5194
5195     #
5196     # Record traversal and update
5197     #
5198
5199     def _mapped_func(self, func):
5200         """ Apply function `func` on all records in `self`, and return the
5201             result as a list or a recordset (if `func` return recordsets).
5202         """
5203         vals = [func(rec) for rec in self]
5204         val0 = vals[0] if vals else func(self)
5205         if isinstance(val0, BaseModel):
5206             return reduce(operator.or_, vals, val0)
5207         return vals
5208
5209     def mapped(self, func):
5210         """ Apply `func` on all records in `self`, and return the result as a
5211             list or a recordset (if `func` return recordsets). In the latter
5212             case, the order of the returned recordset is arbritrary.
5213
5214             :param func: a function or a dot-separated sequence of field names
5215         """
5216         if isinstance(func, basestring):
5217             recs = self
5218             for name in func.split('.'):
5219                 recs = recs._mapped_func(operator.itemgetter(name))
5220             return recs
5221         else:
5222             return self._mapped_func(func)
5223
5224     def _mapped_cache(self, name_seq):
5225         """ Same as `~.mapped`, but `name_seq` is a dot-separated sequence of
5226             field names, and only cached values are used.
5227         """
5228         recs = self
5229         for name in name_seq.split('.'):
5230             field = recs._fields[name]
5231             null = field.null(self.env)
5232             recs = recs.mapped(lambda rec: rec._cache.get(field, null))
5233         return recs
5234
5235     def filtered(self, func):
5236         """ Select the records in `self` such that `func(rec)` is true, and
5237             return them as a recordset.
5238
5239             :param func: a function or a dot-separated sequence of field names
5240         """
5241         if isinstance(func, basestring):
5242             name = func
5243             func = lambda rec: filter(None, rec.mapped(name))
5244         return self.browse([rec.id for rec in self if func(rec)])
5245
5246     def sorted(self, key=None):
5247         """ Return the recordset `self` ordered by `key` """
5248         if key is None:
5249             return self.search([('id', 'in', self.ids)])
5250         else:
5251             return self.browse(map(int, sorted(self, key=key)))
5252
5253     def update(self, values):
5254         """ Update record `self[0]` with `values`. """
5255         for name, value in values.iteritems():
5256             self[name] = value
5257
5258     #
5259     # New records - represent records that do not exist in the database yet;
5260     # they are used to perform onchanges.
5261     #
5262
5263     @api.model
5264     def new(self, values={}):
5265         """ new([values]) -> record
5266
5267         Return a new record instance attached to the current environment and
5268         initialized with the provided ``value``. The record is *not* created
5269         in database, it only exists in memory.
5270         """
5271         record = self.browse([NewId()])
5272         record._cache.update(record._convert_to_cache(values, update=True))
5273
5274         if record.env.in_onchange:
5275             # The cache update does not set inverse fields, so do it manually.
5276             # This is useful for computing a function field on secondary
5277             # records, if that field depends on the main record.
5278             for name in values:
5279                 field = self._fields.get(name)
5280                 if field:
5281                     for invf in field.inverse_fields:
5282                         invf._update(record[name], record)
5283
5284         return record
5285
5286     #
5287     # Dirty flag, to mark records modified (in draft mode)
5288     #
5289
5290     @property
5291     def _dirty(self):
5292         """ Return whether any record in `self` is dirty. """
5293         dirty = self.env.dirty
5294         return any(record in dirty for record in self)
5295
5296     @_dirty.setter
5297     def _dirty(self, value):
5298         """ Mark the records in `self` as dirty. """
5299         if value:
5300             map(self.env.dirty.add, self)
5301         else:
5302             map(self.env.dirty.discard, self)
5303
5304     #
5305     # "Dunder" methods
5306     #
5307
5308     def __nonzero__(self):
5309         """ Test whether `self` is nonempty. """
5310         return bool(getattr(self, '_ids', True))
5311
5312     def __len__(self):
5313         """ Return the size of `self`. """
5314         return len(self._ids)
5315
5316     def __iter__(self):
5317         """ Return an iterator over `self`. """
5318         for id in self._ids:
5319             yield self._browse(self.env, (id,))
5320
5321     def __contains__(self, item):
5322         """ Test whether `item` (record or field name) is an element of `self`.
5323             In the first case, the test is fully equivalent to::
5324
5325                 any(item == record for record in self)
5326         """
5327         if isinstance(item, BaseModel) and self._name == item._name:
5328             return len(item) == 1 and item.id in self._ids
5329         elif isinstance(item, basestring):
5330             return item in self._fields
5331         else:
5332             raise except_orm("ValueError", "Mixing apples and oranges: %s in %s" % (item, self))
5333
5334     def __add__(self, other):
5335         """ Return the concatenation of two recordsets. """
5336         if not isinstance(other, BaseModel) or self._name != other._name:
5337             raise except_orm("ValueError", "Mixing apples and oranges: %s + %s" % (self, other))
5338         return self.browse(self._ids + other._ids)
5339
5340     def __sub__(self, other):
5341         """ Return the recordset of all the records in `self` that are not in `other`. """
5342         if not isinstance(other, BaseModel) or self._name != other._name:
5343             raise except_orm("ValueError", "Mixing apples and oranges: %s - %s" % (self, other))
5344         other_ids = set(other._ids)
5345         return self.browse([id for id in self._ids if id not in other_ids])
5346
5347     def __and__(self, other):
5348         """ Return the intersection of two recordsets.
5349             Note that recordset order is not preserved.
5350         """
5351         if not isinstance(other, BaseModel) or self._name != other._name:
5352             raise except_orm("ValueError", "Mixing apples and oranges: %s & %s" % (self, other))
5353         return self.browse(set(self._ids) & set(other._ids))
5354
5355     def __or__(self, other):
5356         """ Return the union of two recordsets.
5357             Note that recordset order is not preserved.
5358         """
5359         if not isinstance(other, BaseModel) or self._name != other._name:
5360             raise except_orm("ValueError", "Mixing apples and oranges: %s | %s" % (self, other))
5361         return self.browse(set(self._ids) | set(other._ids))
5362
5363     def __eq__(self, other):
5364         """ Test whether two recordsets are equivalent (up to reordering). """
5365         if not isinstance(other, BaseModel):
5366             if other:
5367                 _logger.warning("Comparing apples and oranges: %s == %s", self, other)
5368             return False
5369         return self._name == other._name and set(self._ids) == set(other._ids)
5370
5371     def __ne__(self, other):
5372         return not self == other
5373
5374     def __lt__(self, other):
5375         if not isinstance(other, BaseModel) or self._name != other._name:
5376             raise except_orm("ValueError", "Mixing apples and oranges: %s < %s" % (self, other))
5377         return set(self._ids) < set(other._ids)
5378
5379     def __le__(self, other):
5380         if not isinstance(other, BaseModel) or self._name != other._name:
5381             raise except_orm("ValueError", "Mixing apples and oranges: %s <= %s" % (self, other))
5382         return set(self._ids) <= set(other._ids)
5383
5384     def __gt__(self, other):
5385         if not isinstance(other, BaseModel) or self._name != other._name:
5386             raise except_orm("ValueError", "Mixing apples and oranges: %s > %s" % (self, other))
5387         return set(self._ids) > set(other._ids)
5388
5389     def __ge__(self, other):
5390         if not isinstance(other, BaseModel) or self._name != other._name:
5391             raise except_orm("ValueError", "Mixing apples and oranges: %s >= %s" % (self, other))
5392         return set(self._ids) >= set(other._ids)
5393
5394     def __int__(self):
5395         return self.id
5396
5397     def __str__(self):
5398         return "%s%s" % (self._name, getattr(self, '_ids', ""))
5399
5400     def __unicode__(self):
5401         return unicode(str(self))
5402
5403     __repr__ = __str__
5404
5405     def __hash__(self):
5406         if hasattr(self, '_ids'):
5407             return hash((self._name, frozenset(self._ids)))
5408         else:
5409             return hash(self._name)
5410
5411     def __getitem__(self, key):
5412         """ If `key` is an integer or a slice, return the corresponding record
5413             selection as an instance (attached to `self.env`).
5414             Otherwise read the field `key` of the first record in `self`.
5415
5416             Examples::
5417
5418                 inst = model.search(dom)    # inst is a recordset
5419                 r4 = inst[3]                # fourth record in inst
5420                 rs = inst[10:20]            # subset of inst
5421                 nm = rs['name']             # name of first record in inst
5422         """
5423         if isinstance(key, basestring):
5424             # important: one must call the field's getter
5425             return self._fields[key].__get__(self, type(self))
5426         elif isinstance(key, slice):
5427             return self._browse(self.env, self._ids[key])
5428         else:
5429             return self._browse(self.env, (self._ids[key],))
5430
5431     def __setitem__(self, key, value):
5432         """ Assign the field `key` to `value` in record `self`. """
5433         # important: one must call the field's setter
5434         return self._fields[key].__set__(self, value)
5435
5436     #
5437     # Cache and recomputation management
5438     #
5439
5440     @lazy_property
5441     def _cache(self):
5442         """ Return the cache of `self`, mapping field names to values. """
5443         return RecordCache(self)
5444
5445     @api.model
5446     def _in_cache_without(self, field):
5447         """ Make sure `self` is present in cache (for prefetching), and return
5448             the records of model `self` in cache that have no value for `field`
5449             (:class:`Field` instance).
5450         """
5451         env = self.env
5452         prefetch_ids = env.prefetch[self._name]
5453         prefetch_ids.update(self._ids)
5454         ids = filter(None, prefetch_ids - set(env.cache[field]))
5455         return self.browse(ids)
5456
5457     @api.model
5458     def refresh(self):
5459         """ Clear the records cache.
5460
5461             .. deprecated:: 8.0
5462                 The record cache is automatically invalidated.
5463         """
5464         self.invalidate_cache()
5465
5466     @api.model
5467     def invalidate_cache(self, fnames=None, ids=None):
5468         """ Invalidate the record caches after some records have been modified.
5469             If both `fnames` and `ids` are ``None``, the whole cache is cleared.
5470
5471             :param fnames: the list of modified fields, or ``None`` for all fields
5472             :param ids: the list of modified record ids, or ``None`` for all
5473         """
5474         if fnames is None:
5475             if ids is None:
5476                 return self.env.invalidate_all()
5477             fields = self._fields.values()
5478         else:
5479             fields = map(self._fields.__getitem__, fnames)
5480
5481         # invalidate fields and inverse fields, too
5482         spec = [(f, ids) for f in fields] + \
5483                [(invf, None) for f in fields for invf in f.inverse_fields]
5484         self.env.invalidate(spec)
5485
5486     @api.multi
5487     def modified(self, fnames):
5488         """ Notify that fields have been modified on `self`. This invalidates
5489             the cache, and prepares the recomputation of stored function fields
5490             (new-style fields only).
5491
5492             :param fnames: iterable of field names that have been modified on
5493                 records `self`
5494         """
5495         # each field knows what to invalidate and recompute
5496         spec = []
5497         for fname in fnames:
5498             spec += self._fields[fname].modified(self)
5499
5500         cached_fields = {
5501             field
5502             for env in self.env.all
5503             for field in env.cache
5504         }
5505         # invalidate non-stored fields.function which are currently cached
5506         spec += [(f, None) for f in self.pool.pure_function_fields
5507                  if f in cached_fields]
5508
5509         self.env.invalidate(spec)
5510
5511     def _recompute_check(self, field):
5512         """ If `field` must be recomputed on some record in `self`, return the
5513             corresponding records that must be recomputed.
5514         """
5515         return self.env.check_todo(field, self)
5516
5517     def _recompute_todo(self, field):
5518         """ Mark `field` to be recomputed. """
5519         self.env.add_todo(field, self)
5520
5521     def _recompute_done(self, field):
5522         """ Mark `field` as recomputed. """
5523         self.env.remove_todo(field, self)
5524
5525     @api.model
5526     def recompute(self):
5527         """ Recompute stored function fields. The fields and records to
5528             recompute have been determined by method :meth:`modified`.
5529         """
5530         while self.env.has_todo():
5531             field, recs = self.env.get_todo()
5532             # evaluate the fields to recompute, and save them to database
5533             for rec, rec1 in zip(recs, recs.with_context(recompute=False)):
5534                 try:
5535                     values = rec._convert_to_write({
5536                         f.name: rec[f.name] for f in field.computed_fields
5537                     })
5538                     rec1._write(values)
5539                 except MissingError:
5540                     pass
5541             # mark the computed fields as done
5542             map(recs._recompute_done, field.computed_fields)
5543
5544     #
5545     # Generic onchange method
5546     #
5547
5548     def _has_onchange(self, field, other_fields):
5549         """ Return whether `field` should trigger an onchange event in the
5550             presence of `other_fields`.
5551         """
5552         # test whether self has an onchange method for field, or field is a
5553         # dependency of any field in other_fields
5554         return field.name in self._onchange_methods or \
5555             any(dep in other_fields for dep in field.dependents)
5556
5557     @api.model
5558     def _onchange_spec(self, view_info=None):
5559         """ Return the onchange spec from a view description; if not given, the
5560             result of ``self.fields_view_get()`` is used.
5561         """
5562         result = {}
5563
5564         # for traversing the XML arch and populating result
5565         def process(node, info, prefix):
5566             if node.tag == 'field':
5567                 name = node.attrib['name']
5568                 names = "%s.%s" % (prefix, name) if prefix else name
5569                 if not result.get(names):
5570                     result[names] = node.attrib.get('on_change')
5571                 # traverse the subviews included in relational fields
5572                 for subinfo in info['fields'][name].get('views', {}).itervalues():
5573                     process(etree.fromstring(subinfo['arch']), subinfo, names)
5574             else:
5575                 for child in node:
5576                     process(child, info, prefix)
5577
5578         if view_info is None:
5579             view_info = self.fields_view_get()
5580         process(etree.fromstring(view_info['arch']), view_info, '')
5581         return result
5582
5583     def _onchange_eval(self, field_name, onchange, result):
5584         """ Apply onchange method(s) for field `field_name` with spec `onchange`
5585             on record `self`. Value assignments are applied on `self`, while
5586             domain and warning messages are put in dictionary `result`.
5587         """
5588         onchange = onchange.strip()
5589
5590         # onchange V8
5591         if onchange in ("1", "true"):
5592             for method in self._onchange_methods.get(field_name, ()):
5593                 method_res = method(self)
5594                 if not method_res:
5595                     continue
5596                 if 'domain' in method_res:
5597                     result.setdefault('domain', {}).update(method_res['domain'])
5598                 if 'warning' in method_res:
5599                     result['warning'] = method_res['warning']
5600             return
5601
5602         # onchange V7
5603         match = onchange_v7.match(onchange)
5604         if match:
5605             method, params = match.groups()
5606
5607             # evaluate params -> tuple
5608             global_vars = {'context': self._context, 'uid': self._uid}
5609             if self._context.get('field_parent'):
5610                 class RawRecord(object):
5611                     def __init__(self, record):
5612                         self._record = record
5613                     def __getattr__(self, name):
5614                         field = self._record._fields[name]
5615                         value = self._record[name]
5616                         return field.convert_to_onchange(value)
5617                 record = self[self._context['field_parent']]
5618                 global_vars['parent'] = RawRecord(record)
5619             field_vars = {
5620                 key: self._fields[key].convert_to_onchange(val)
5621                 for key, val in self._cache.iteritems()
5622             }
5623             params = eval("[%s]" % params, global_vars, field_vars)
5624
5625             # call onchange method
5626             args = (self._cr, self._uid, self._origin.ids) + tuple(params)
5627             method_res = getattr(self._model, method)(*args)
5628             if not isinstance(method_res, dict):
5629                 return
5630             if 'value' in method_res:
5631                 method_res['value'].pop('id', None)
5632                 self.update(self._convert_to_cache(method_res['value'], validate=False))
5633             if 'domain' in method_res:
5634                 result.setdefault('domain', {}).update(method_res['domain'])
5635             if 'warning' in method_res:
5636                 result['warning'] = method_res['warning']
5637
5638     @api.multi
5639     def onchange(self, values, field_name, field_onchange):
5640         """ Perform an onchange on the given field.
5641
5642             :param values: dictionary mapping field names to values, giving the
5643                 current state of modification
5644             :param field_name: name of the modified field_name
5645             :param field_onchange: dictionary mapping field names to their
5646                 on_change attribute
5647         """
5648         env = self.env
5649
5650         if field_name and field_name not in self._fields:
5651             return {}
5652
5653         # determine subfields for field.convert_to_write() below
5654         secondary = []
5655         subfields = defaultdict(set)
5656         for dotname in field_onchange:
5657             if '.' in dotname:
5658                 secondary.append(dotname)
5659                 name, subname = dotname.split('.')
5660                 subfields[name].add(subname)
5661
5662         # create a new record with values, and attach `self` to it
5663         with env.do_in_onchange():
5664             record = self.new(values)
5665             values = dict(record._cache)
5666             # attach `self` with a different context (for cache consistency)
5667             record._origin = self.with_context(__onchange=True)
5668
5669         # determine which field should be triggered an onchange
5670         todo = set([field_name]) if field_name else set(values)
5671         done = set()
5672
5673         # dummy assignment: trigger invalidations on the record
5674         for name in todo:
5675             value = record[name]
5676             field = self._fields[name]
5677             if not field_name and field.type == 'many2one' and field.delegate and not value:
5678                 # do not nullify all fields of parent record for new records
5679                 continue
5680             record[name] = value
5681
5682         result = {'value': {}}
5683
5684         while todo:
5685             name = todo.pop()
5686             if name in done:
5687                 continue
5688             done.add(name)
5689
5690             with env.do_in_onchange():
5691                 # apply field-specific onchange methods
5692                 if field_onchange.get(name):
5693                     record._onchange_eval(name, field_onchange[name], result)
5694
5695                 # force re-evaluation of function fields on secondary records
5696                 for field_seq in secondary:
5697                     record.mapped(field_seq)
5698
5699                 # determine which fields have been modified
5700                 for name, oldval in values.iteritems():
5701                     field = self._fields[name]
5702                     newval = record[name]
5703                     if field.type in ('one2many', 'many2many'):
5704                         if newval != oldval or newval._dirty:
5705                             # put new value in result
5706                             result['value'][name] = field.convert_to_write(
5707                                 newval, record._origin, subfields.get(name),
5708                             )
5709                             todo.add(name)
5710                         else:
5711                             # keep result: newval may have been dirty before
5712                             pass
5713                     else:
5714                         if newval != oldval:
5715                             # put new value in result
5716                             result['value'][name] = field.convert_to_write(
5717                                 newval, record._origin, subfields.get(name),
5718                             )
5719                             todo.add(name)
5720                         else:
5721                             # clean up result to not return another value
5722                             result['value'].pop(name, None)
5723
5724         # At the moment, the client does not support updates on a *2many field
5725         # while this one is modified by the user.
5726         if field_name and self._fields[field_name].type in ('one2many', 'many2many'):
5727             result['value'].pop(field_name, None)
5728
5729         return result
5730
5731
5732 class RecordCache(MutableMapping):
5733     """ Implements a proxy dictionary to read/update the cache of a record.
5734         Upon iteration, it looks like a dictionary mapping field names to
5735         values. However, fields may be used as keys as well.
5736     """
5737     def __init__(self, records):
5738         self._recs = records
5739
5740     def contains(self, field):
5741         """ Return whether `records[0]` has a value for `field` in cache. """
5742         if isinstance(field, basestring):
5743             field = self._recs._fields[field]
5744         return self._recs.id in self._recs.env.cache[field]
5745
5746     def __contains__(self, field):
5747         """ Return whether `records[0]` has a regular value for `field` in cache. """
5748         if isinstance(field, basestring):
5749             field = self._recs._fields[field]
5750         dummy = SpecialValue(None)
5751         value = self._recs.env.cache[field].get(self._recs.id, dummy)
5752         return not isinstance(value, SpecialValue)
5753
5754     def __getitem__(self, field):
5755         """ Return the cached value of `field` for `records[0]`. """
5756         if isinstance(field, basestring):
5757             field = self._recs._fields[field]
5758         value = self._recs.env.cache[field][self._recs.id]
5759         return value.get() if isinstance(value, SpecialValue) else value
5760
5761     def __setitem__(self, field, value):
5762         """ Assign the cached value of `field` for all records in `records`. """
5763         if isinstance(field, basestring):
5764             field = self._recs._fields[field]
5765         values = dict.fromkeys(self._recs._ids, value)
5766         self._recs.env.cache[field].update(values)
5767
5768     def update(self, *args, **kwargs):
5769         """ Update the cache of all records in `records`. If the argument is a
5770             `SpecialValue`, update all fields (except "magic" columns).
5771         """
5772         if args and isinstance(args[0], SpecialValue):
5773             values = dict.fromkeys(self._recs._ids, args[0])
5774             for name, field in self._recs._fields.iteritems():
5775                 if name != 'id':
5776                     self._recs.env.cache[field].update(values)
5777         else:
5778             return super(RecordCache, self).update(*args, **kwargs)
5779
5780     def __delitem__(self, field):
5781         """ Remove the cached value of `field` for all `records`. """
5782         if isinstance(field, basestring):
5783             field = self._recs._fields[field]
5784         field_cache = self._recs.env.cache[field]
5785         for id in self._recs._ids:
5786             field_cache.pop(id, None)
5787
5788     def __iter__(self):
5789         """ Iterate over the field names with a regular value in cache. """
5790         cache, id = self._recs.env.cache, self._recs.id
5791         dummy = SpecialValue(None)
5792         for name, field in self._recs._fields.iteritems():
5793             if name != 'id' and not isinstance(cache[field].get(id, dummy), SpecialValue):
5794                 yield name
5795
5796     def __len__(self):
5797         """ Return the number of fields with a regular value in cache. """
5798         return sum(1 for name in self)
5799
5800 class Model(BaseModel):
5801     """Main super-class for regular database-persisted OpenERP models.
5802
5803     OpenERP models are created by inheriting from this class::
5804
5805         class user(Model):
5806             ...
5807
5808     The system will later instantiate the class once per database (on
5809     which the class' module is installed).
5810     """
5811     _auto = True
5812     _register = False # not visible in ORM registry, meant to be python-inherited only
5813     _transient = False # True in a TransientModel
5814
5815 class TransientModel(BaseModel):
5816     """Model super-class for transient records, meant to be temporarily
5817        persisted, and regularly vaccuum-cleaned.
5818
5819        A TransientModel has a simplified access rights management,
5820        all users can create new records, and may only access the
5821        records they created. The super-user has unrestricted access
5822        to all TransientModel records.
5823     """
5824     _auto = True
5825     _register = False # not visible in ORM registry, meant to be python-inherited only
5826     _transient = True
5827
5828 class AbstractModel(BaseModel):
5829     """Abstract Model super-class for creating an abstract class meant to be
5830        inherited by regular models (Models or TransientModels) but not meant to
5831        be usable on its own, or persisted.
5832
5833        Technical note: we don't want to make AbstractModel the super-class of
5834        Model or BaseModel because it would not make sense to put the main
5835        definition of persistence methods such as create() in it, and still we
5836        should be able to override them within an AbstractModel.
5837        """
5838     _auto = False # don't create any database backend for AbstractModels
5839     _register = False # not visible in ORM registry, meant to be python-inherited only
5840     _transient = False
5841
5842 def itemgetter_tuple(items):
5843     """ Fixes itemgetter inconsistency (useful in some cases) of not returning
5844     a tuple if len(items) == 1: always returns an n-tuple where n = len(items)
5845     """
5846     if len(items) == 0:
5847         return lambda a: ()
5848     if len(items) == 1:
5849         return lambda gettable: (gettable[items[0]],)
5850     return operator.itemgetter(*items)
5851
5852 def convert_pgerror_23502(model, fields, info, e):
5853     m = re.match(r'^null value in column "(?P<field>\w+)" violates '
5854                  r'not-null constraint\n',
5855                  str(e))
5856     field_name = m and m.group('field')
5857     if not m or field_name not in fields:
5858         return {'message': unicode(e)}
5859     message = _(u"Missing required value for the field '%s'.") % field_name
5860     field = fields.get(field_name)
5861     if field:
5862         message = _(u"Missing required value for the field '%s' (%s)") % (field['string'], field_name)
5863     return {
5864         'message': message,
5865         'field': field_name,
5866     }
5867
5868 def convert_pgerror_23505(model, fields, info, e):
5869     m = re.match(r'^duplicate key (?P<field>\w+) violates unique constraint',
5870                  str(e))
5871     field_name = m and m.group('field')
5872     if not m or field_name not in fields:
5873         return {'message': unicode(e)}
5874     message = _(u"The value for the field '%s' already exists.") % field_name
5875     field = fields.get(field_name)
5876     if field:
5877         message = _(u"%s This might be '%s' in the current model, or a field "
5878                     u"of the same name in an o2m.") % (message, field['string'])
5879     return {
5880         'message': message,
5881         'field': field_name,
5882     }
5883
5884 PGERROR_TO_OE = defaultdict(
5885     # shape of mapped converters
5886     lambda: (lambda model, fvg, info, pgerror: {'message': unicode(pgerror)}), {
5887     # not_null_violation
5888     '23502': convert_pgerror_23502,
5889     # unique constraint error
5890     '23505': convert_pgerror_23505,
5891 })
5892
5893 def _normalize_ids(arg, atoms={int, long, str, unicode, NewId}):
5894     """ Normalizes the ids argument for ``browse`` (v7 and v8) to a tuple.
5895
5896     Various implementations were tested on the corpus of all browse() calls
5897     performed during a full crawler run (after having installed all website_*
5898     modules) and this one was the most efficient overall.
5899
5900     A possible bit of correctness was sacrificed by not doing any test on
5901     Iterable and just assuming that any non-atomic type was an iterable of
5902     some kind.
5903
5904     :rtype: tuple
5905     """
5906     # much of the corpus is falsy objects (empty list, tuple or set, None)
5907     if not arg:
5908         return ()
5909
5910     # `type in set` is significantly faster (because more restrictive) than
5911     # isinstance(arg, set) or issubclass(type, set); and for new-style classes
5912     # obj.__class__ is equivalent to but faster than type(obj). Not relevant
5913     # (and looks much worse) in most cases, but over millions of calls it
5914     # does have a very minor effect.
5915     if arg.__class__ in atoms:
5916         return arg,
5917
5918     return tuple(arg)
5919
5920 # keep those imports here to avoid dependency cycle errors
5921 from .osv import expression
5922 from .fields import Field, SpecialValue, FailedValue
5923
5924 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: