X-Git-Url: http://git.inspyration.org/?a=blobdiff_plain;f=bin%2Fosv%2Form.py;h=92fb7f9400bacd0e4a36735d1157c4cf6ceb5f75;hb=4f09c9ffa5ef1c45b8480d17fe4af72f74f6edcd;hp=5c59ae3abb922001e051600a0addaa106a4b57ae;hpb=852429f16283ffe0c6ffc84fe26dc29db1cbf8a1;p=odoo%2Fodoo.git diff --git a/bin/osv/orm.py b/bin/osv/orm.py index 5c59ae3..92fb7f9 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -41,6 +41,7 @@ import calendar import copy import datetime +import logging import pickle import random import re @@ -49,15 +50,33 @@ import sys import time import traceback import types -from lxml import etree import fields import netsvc import tools -from tools.config import config from tools.translate import _ -regex_order = re.compile('^([a-z0-9_]+( *desc| *asc)?( *, *|))+$', re.I) +import copy +import sys + +try: + from lxml import etree +except ImportError: + sys.stderr.write("ERROR: Import lxml module\n") + sys.stderr.write("ERROR: Try to install the python-lxml package\n") + +from tools.config import config + +regex_order = re.compile('^(([a-z0-9_]+|"[a-z0-9_]+")( *desc| *asc)?( *, *|))+$', re.I) + + +POSTGRES_CONFDELTYPES = { + 'RESTRICT': 'r', + 'NO ACTION': 'a', + 'CASCADE': 'c', + 'SET NULL': 'n', + 'SET DEFAULT': 'd', +} def last_day_of_current_month(): today = datetime.date.today() @@ -130,6 +149,8 @@ class browse_record(object): self._id = id self._table = table self._table_name = self._table._name + self.__logger = logging.getLogger( + 'osv.browse_record.' + self._table_name) self._context = context self._fields_process = fields_process @@ -201,14 +222,14 @@ class browse_record(object): if not datas: # Where did those ids come from? Perhaps old entries in ir_model_dat? - self.logger.notifyChannel("browse_record", netsvc.LOG_WARNING, - "No datas found for ids %s in %s \n%s" % ( - ids, self, ''.join(traceback.format_exc()))) + self.__logger.warn("No datas found for ids %s in %s", + ids, self) raise KeyError('Field %s not found in %s'%(name,self)) # create browse records for 'remote' objects for data in datas: if len(str(data['id']).split('-')) > 1: data['id'] = int(str(data['id']).split('-')[0]) + new_data = {} for n, f in ffields: if f._type in ('many2one', 'one2one'): if data[n]: @@ -219,14 +240,37 @@ class browse_record(object): else: ids2 = data[n] if ids2: - data[n] = browse_record(self._cr, self._uid, ids2, obj, self._cache, context=self._context, list_class=self._list_class, fields_process=self._fields_process) + # FIXME: this happen when a _inherits object + # overwrite a field of it parent. Need + # testing to be sure we got the right + # object and not the parent one. + if not isinstance(ids2, browse_record): + new_data[n] = browse_record(self._cr, + self._uid, ids2, obj, self._cache, + context=self._context, + list_class=self._list_class, + fields_process=self._fields_process) else: - data[n] = browse_null() + new_data[n] = browse_null() else: - data[n] = browse_null() + new_data[n] = browse_null() elif f._type in ('one2many', 'many2many') and len(data[n]): - data[n] = self._list_class([browse_record(self._cr, self._uid, id, self._table.pool.get(f._obj), self._cache, context=self._context, list_class=self._list_class, fields_process=self._fields_process) for id in data[n]], self._context) - self._data[data['id']].update(data) + new_data[n] = self._list_class([browse_record(self._cr, self._uid, id, self._table.pool.get(f._obj), self._cache, context=self._context, list_class=self._list_class, fields_process=self._fields_process) for id in data[n]], self._context) + elif f._type in ('reference'): + if data[n]: + if isinstance(data[n], browse_record): + new_data[n] = data[n] + else: + ref_obj, ref_id = data[n].split(',') + ref_id = long(ref_id) + obj = self._table.pool.get(ref_obj) + compids = False + new_data[n] = browse_record(self._cr, self._uid, ref_id, obj, self._cache, context=self._context, list_class=self._list_class, fields_process=self._fields_process) + else: + new_data[n] = browse_null() + else: + new_data[n] = data[n] + self._data[data['id']].update(new_data) if not name in self._data[self._id]: #how did this happen? self.logger.notifyChannel("browse_record", netsvc.LOG_ERROR, @@ -292,7 +336,7 @@ def get_pg_type(f): f_type = (type_dict[type(f)], type_dict[type(f)]) elif isinstance(f, fields.float): if f.digits: - f_type = ('numeric', 'NUMERIC(%d,%d)' % (f.digits[0], f.digits[1])) + f_type = ('numeric', 'NUMERIC') else: f_type = ('float8', 'DOUBLE PRECISION') elif isinstance(f, (fields.char, fields.reference)): @@ -314,7 +358,7 @@ def get_pg_type(f): f_type = (type_dict[t], type_dict[t]) elif isinstance(f, fields.function) and f._type == 'float': if f.digits: - f_type = ('numeric', 'NUMERIC(%d,%d)' % (f.digits[0], f.digits[1])) + f_type = ('numeric', 'NUMERIC') else: f_type = ('float8', 'DOUBLE PRECISION') elif isinstance(f, fields.function) and f._type == 'selection': @@ -347,6 +391,10 @@ class orm_template(object): CONCURRENCY_CHECK_FIELD = '__last_update' + def view_init(self, cr , uid , fields_list, context=None): + """Override this method to do specific things when a view on the object is opened.""" + pass + def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None): raise _('The read_group method is not implemented on this object !') @@ -360,7 +408,7 @@ class orm_template(object): model_id = cr.fetchone()[0] if 'module' in context: name_id = 'model_'+self._name.replace('.','_') - cr.execute('select * from ir_model_data where name=%s and res_id=%s', (name_id,model_id)) + cr.execute('select * from ir_model_data where name=%s and res_id=%s and module=%s', (name_id,model_id,context['module'])) if not cr.rowcount: cr.execute("INSERT INTO ir_model_data (name,date_init,date_update,module,model,res_id) VALUES (%s, now(), now(), %s, %s, %s)", \ (name_id, context['module'], 'ir.model', model_id) @@ -386,6 +434,7 @@ class orm_template(object): 'readonly':(f.readonly and 1) or 0, 'required':(f.required and 1) or 0, 'selectable' : (f.selectable and 1) or 0, + 'relation_field': (f._type=='one2many' and isinstance(f,fields.one2many)) and f._fields_id or '', } # When its a custom field,it does not contain f.select if context.get('field_state','base') == 'manual': @@ -401,13 +450,13 @@ class orm_template(object): vals['id'] = id cr.execute("""INSERT INTO ir_model_fields ( id, model_id, model, name, field_description, ttype, - relation,view_load,state,select_level + relation,view_load,state,select_level,relation_field ) VALUES ( - %s,%s,%s,%s,%s,%s,%s,%s,%s,%s + %s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s )""", ( id, vals['model_id'], vals['model'], vals['name'], vals['field_description'], vals['ttype'], vals['relation'], bool(vals['view_load']), 'base', - vals['select_level'] + vals['select_level'],vals['relation_field'] )) if 'module' in context: name1 = 'field_' + self._table + '_' + k @@ -424,12 +473,12 @@ class orm_template(object): cr.commit() cr.execute("""UPDATE ir_model_fields SET model_id=%s, field_description=%s, ttype=%s, relation=%s, - view_load=%s, select_level=%s, readonly=%s ,required=%s, selectable=%s + view_load=%s, select_level=%s, readonly=%s ,required=%s, selectable=%s, relation_field=%s WHERE model=%s AND name=%s""", ( vals['model_id'], vals['field_description'], vals['ttype'], vals['relation'], bool(vals['view_load']), - vals['select_level'], bool(vals['readonly']),bool(vals['required']),bool(vals['selectable']),vals['model'], vals['name'] + vals['select_level'], bool(vals['readonly']),bool(vals['required']),bool(vals['selectable']),vals['relation_field'],vals['model'], vals['name'] )) continue cr.commit() @@ -687,6 +736,7 @@ class orm_template(object): else: module, xml_id = current_module, line[i] id = ir_model_data_obj._get_id(cr, uid, module, xml_id) + res_res_id = ir_model_data_obj.read(cr, uid, [id], ['res_id']) if res_res_id: @@ -883,6 +933,8 @@ class orm_template(object): if isinstance(e, osv.orm.except_orm ): msg = _('Insertion Failed! ' + e[1]) return (-1, res, 'Line ' + str(counter) +' : ' + msg, '' ) + #Raising Uncaught exception + raise for lang in translate: context2 = context.copy() context2['lang'] = lang @@ -981,8 +1033,8 @@ class orm_template(object): if fields and f not in fields: continue res[f] = {'type': self._columns[f]._type} - for arg in ('string', 'readonly', 'states', 'size', 'required', - 'change_default', 'translate', 'help', 'select', 'selectable'): + for arg in ('string', 'readonly', 'states', 'size', 'required', 'group_operator', + 'change_default', 'translate', 'help', 'select', 'selectable','parent_field'): if getattr(self._columns[f], arg): res[f][arg] = getattr(self._columns[f], arg) if not read_access: @@ -1076,7 +1128,7 @@ class orm_template(object): if column._domain and not isinstance(column._domain, (str, unicode)): dom = column._domain dom += eval(node.get('domain','[]'), {'uid':user, 'time':time}) - context.update(eval(node.get('context','{}'))) + context.update(eval(node.get('context','{}'))) attrs['selection'] = self.pool.get(relation).name_search(cr, user, '', dom, context=context) if (node.get('required') and not int(node.get('required'))) or not column.required: attrs['selection'].append((False,'')) @@ -1162,7 +1214,21 @@ class orm_template(object): button.set('readonly', str(int(not can_click))) arch = etree.tostring(node, encoding="utf-8").replace('\t', '') - fields = self.fields_get(cr, user, fields_def.keys(), context) + + #code for diagram view. + fields={} + if node.tag=='diagram': + if node.getchildren()[0].tag=='node': + node_fields=self.pool.get(node.getchildren()[0].get('object')).fields_get(cr, user, fields_def.keys(), context) + if node.getchildren()[1].tag=='arrow': + arrow_fields = self.pool.get(node.getchildren()[1].get('object')).fields_get(cr, user, fields_def.keys(), context) + for key,value in node_fields.items(): + fields[key]=value + for key,value in arrow_fields.items(): + fields[key]=value + else: + fields = self.fields_get(cr, user, fields_def.keys(), context) + for field in fields_def: if field == 'id': # sometime, the view may containt the (invisible) field 'id' needed for a domain (when 2 objects have cross references) @@ -1178,7 +1244,6 @@ class orm_template(object): msg += "\n\nEither you wrongly customised this view, or some modules bringing those views are not compatible with your current data model" netsvc.Logger().notifyChannel('orm', netsvc.LOG_ERROR, msg) raise except_orm('View error', msg) - return arch, fields def __get_default_calendar_view(self): @@ -1437,7 +1502,7 @@ class orm_template(object): if context and context.get('active_id',False): data_menu = self.pool.get('ir.ui.menu').browse(cr, user, context['active_id'], context).action if data_menu: - act_id = int(data_menu.split(',')[1]) + act_id = data_menu.id if act_id: data_action = self.pool.get('ir.actions.act_window').browse(cr, user, [act_id], context)[0] result['submenu'] = getattr(data_action,'menus', False) @@ -1462,7 +1527,6 @@ class orm_template(object): 'client_action_relate', [(self._name, False)], False, context) resprint = map(clean, resprint) - print "resprintresprint",resprint resaction = map(clean, resaction) resaction = filter(lambda x: not x.get('multi', False), resaction) resprint = filter(lambda x: not x.get('multi', False), resprint) @@ -1476,6 +1540,10 @@ class orm_template(object): 'action': resaction, 'relate': resrelate } + if result['type']=='form' and result['arch'].count("default_focus")>1: + msg = "Form View contain more than one default_focus attribute" + netsvc.Logger().notifyChannel('orm', netsvc.LOG_ERROR, msg) + raise except_orm('View Error !',msg) return result _view_look_dom_arch = __view_look_dom_arch @@ -1654,6 +1722,7 @@ class orm_memory(orm_template): return id_new def default_get(self, cr, uid, fields_list, context=None): + self.view_init(cr, uid, fields_list, context) if not context: context = {} value = {} @@ -1809,22 +1878,29 @@ class orm(orm_template): _protected = ['read','write','create','default_get','perm_read','unlink','fields_get','fields_view_get','search','name_get','distinct_field_get','name_search','copy','import_data','search_count', 'exists'] def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None): + groupby_list = groupby + if isinstance(groupby, list): + groupby = groupby[0] context = context or {} self.pool.get('ir.model.access').check(cr, uid, self._name, 'read', context=context) if not fields: fields = self._columns.keys() - (qu1, qu2, tables) = self._where_calc(cr, uid, domain, context=context) - dom = self.pool.get('ir.rule').domain_get(cr, uid, self._name, context=context) - qu1 = qu1 + dom[0] - qu2 = qu2 + dom[1] + (where_clause, where_params, tables) = self._where_calc(cr, uid, domain, context=context) + dom = self.pool.get('ir.rule').domain_get(cr, uid, self._name, 'read', context=context) + where_clause = where_clause + dom[0] + where_params = where_params + dom[1] for t in dom[2]: if t not in tables: tables.append(t) - if len(qu1): - qu1 = ' where '+string.join(qu1, ' and ') + + # Take care of adding join(s) if groupby is an '_inherits'ed field + tables, where_clause = self._inherits_join_calc(groupby,tables,where_clause) + + if len(where_clause): + where_clause = ' where '+string.join(where_clause, ' and ') else: - qu1 = '' + where_clause = '' limit_str = limit and ' limit %d' % limit or '' offset_str = offset and ' offset %d' % offset or '' @@ -1833,7 +1909,7 @@ class orm(orm_template): sum = {} group_by = groupby - if fget[groupby]['type'] in ('date','datetime'): + if fget.get(groupby,False) and fget[groupby]['type'] in ('date','datetime'): flist = "to_char(%s,'yyyy-mm') as %s "%(groupby,groupby) groupby = "to_char(%s,'yyyy-mm')"%(groupby) else: @@ -1842,17 +1918,17 @@ class orm(orm_template): fields_pre = [f for f in float_int_fields if f == self.CONCURRENCY_CHECK_FIELD or (f in self._columns and getattr(self._columns[f], '_classic_write'))] - avg_fields = context.get('avg',[]) for f in fields_pre: if f not in ['id','sequence']: - if f in avg_fields: - flist += ',avg('+f+') as '+f - else: - flist += ',sum('+f+') as '+f - cr.execute('select min(id) as id,'+flist+' from ' + self._table +qu1+' group by '+ groupby + limit_str + offset_str,qu2) + operator = fget[f].get('group_operator','sum') + flist += ','+operator+'('+f+') as '+f + + cr.execute('select min(%s.id) as id,' % self._table + flist + ' from ' + ','.join(tables) + where_clause + ' group by '+ groupby + limit_str + offset_str, where_params) alldata = {} groupby = group_by for r in cr.dictfetchall(): + for fld,val in r.items(): + if val == None:r[fld] = False alldata[r['id']] = r del r['id'] data = self.read(cr, uid, alldata.keys(), [groupby], context=context) @@ -1860,26 +1936,39 @@ class orm(orm_template): for d in data: d['__domain'] = [(groupby,'=',alldata[d['id']][groupby] or False)] + domain + d['__context'] = {'group_by':groupby_list[1:]} if fget.has_key(groupby): if d[groupby] and fget[groupby]['type'] in ('date','datetime'): dt = datetime.datetime.strptime(alldata[d['id']][groupby][:7],'%Y-%m') days = calendar.monthrange(dt.year, dt.month)[1] - if d[groupby][:7] == today.strftime('%Y-%m'): - d[groupby] = 'This Month' - else: - d[groupby] = datetime.datetime.strptime(d[groupby][:10],'%Y-%m-%d').strftime('%B %Y') - + d[groupby] = datetime.datetime.strptime(d[groupby][:10],'%Y-%m-%d').strftime('%B %Y') d['__domain'] = [(groupby,'>=',alldata[d['id']][groupby] and datetime.datetime.strptime(alldata[d['id']][groupby][:7] + '-01','%Y-%m-%d').strftime('%Y-%m-%d') or False),\ (groupby,'<=',alldata[d['id']][groupby] and datetime.datetime.strptime(alldata[d['id']][groupby][:7] + '-' + str(days),'%Y-%m-%d').strftime('%Y-%m-%d') or False)] + domain elif fget[groupby]['type'] == 'many2one': - d[groupby] = d[groupby] and d[groupby][1] or '' + d[groupby] = d[groupby] and ((type(d[groupby])==type(1)) and d[groupby] or d[groupby][1]) or '' del alldata[d['id']][groupby] d.update(alldata[d['id']]) del d['id'] return data + def _inherits_join_calc(self, field, tables, where_clause): + """ Adds missing table select and join clause(s) for reaching + the field coming from an '_inherits' parent table. + @param tables: list of table._table names enclosed in double quotes as returned + by _where_calc() + """ + current_table = self + while field in current_table._inherit_fields and not field in current_table._columns: + parent_table = self.pool.get(current_table._inherit_fields[field][0]) + parent_table_name = parent_table._table + if '"%s"'%parent_table_name not in tables: + tables.append('"%s"'%parent_table_name) + where_clause.append('(%s.%s = %s.id)' % (current_table._table, current_table._inherits[parent_table._name], parent_table_name)) + current_table = parent_table + return (tables, where_clause) + def _parent_store_compute(self, cr): logger = netsvc.Logger() logger.notifyChannel('orm', netsvc.LOG_INFO, 'Computing parent left and right for table %s...' % (self._table, )) @@ -1997,14 +2086,24 @@ class orm(orm_template): # iterate on the "object columns" todo_update_store = [] + update_custom_fields = context.get('update_custom_fields', False) for k in self._columns: if k in ('id', 'write_uid', 'write_date', 'create_uid', 'create_date'): continue #raise _('Can not define a column %s. Reserved keyword !') % (k,) + #Not Updating Custom fields + if k.startswith('x_') and not update_custom_fields: + continue f = self._columns[k] if isinstance(f, fields.one2many): cr.execute("SELECT relname FROM pg_class WHERE relkind='r' AND relname=%s", (f._obj,)) + + if self.pool.get(f._obj): + if f._fields_id not in self.pool.get(f._obj)._columns.keys(): + if not self.pool.get(f._obj)._inherits or (f._fields_id not in self.pool.get(f._obj)._inherit_fields.keys()): + raise except_orm('Programming Error', ("There is no reference field '%s' found for '%s'") % (f._fields_id,f._obj,)) + if cr.fetchone(): cr.execute("SELECT count(1) as c FROM pg_class c,pg_attribute a WHERE c.relname=%s AND a.attname=%s AND c.oid=a.attrelid", (f._obj, f._fields_id)) res = cr.fetchone()[0] @@ -2098,7 +2197,7 @@ class orm(orm_template): if isinstance(f, fields.function) and not f.store and\ not getattr(f, 'nodrop', False): logger.notifyChannel('orm', netsvc.LOG_INFO, 'column %s (%s) in table %s removed: converted to a function !\n' % (k, f.string, self._table)) - cr.execute('ALTER TABLE "%s" DROP COLUMN "%s"'% (self._table, k)) + cr.execute('ALTER TABLE "%s" DROP COLUMN "%s" CASCADE'% (self._table, k)) cr.commit() f_obj_type = None else: @@ -2125,19 +2224,9 @@ class orm(orm_template): cr.commit() for c in casts: if (f_pg_type==c[0]) and (f._type==c[1]): - # Adding upcoming 6 lines to check whether only the size of the fields got changed or not.E.g. :(16,3) to (16,4) - field_size_change = False - if f_pg_type in ['int4','numeric','float8']: - if f.digits: - field_size = (65535 * f.digits[0]) + f.digits[0] + f.digits[1] - if field_size != f_pg_size: - field_size_change = True - - if f_pg_type != f_obj_type or field_size_change: + if f_pg_type != f_obj_type: if f_pg_type != f_obj_type: logger.notifyChannel('orm', netsvc.LOG_INFO, "column '%s' in table '%s' changed type to %s." % (k, self._table, c[1])) - if field_size_change: - logger.notifyChannel('orm', netsvc.LOG_INFO, "column '%s' in table '%s' changed in the size." % (k, self._table)) ok = True cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO temp_change_size' % (self._table, k)) cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, c[2])) @@ -2203,14 +2292,7 @@ class orm(orm_template): "AND con.contype = 'f'", (self._table, ref, k, 'id')) res = cr.dictfetchall() if res: - confdeltype = { - 'RESTRICT': 'r', - 'NO ACTION': 'a', - 'CASCADE': 'c', - 'SET NULL': 'n', - 'SET DEFAULT': 'd', - } - if res[0]['confdeltype'] != confdeltype.get(f.ondelete.upper(), 'a'): + if res[0]['confdeltype'] != POSTGRES_CONFDELTYPES.get(f.ondelete.upper(), 'a'): cr.execute('ALTER TABLE "' + self._table + '" DROP CONSTRAINT "' + res[0]['conname'] + '"') cr.execute('ALTER TABLE "' + self._table + '" ADD FOREIGN KEY ("' + k + '") REFERENCES "' + ref + '" ON DELETE ' + f.ondelete) cr.commit() @@ -2254,6 +2336,8 @@ class orm(orm_template): self._columns = self._columns.copy() for store_field in self._columns: f = self._columns[store_field] + if hasattr(f, 'digits_change'): + f.digits_change(cr) if not isinstance(f, fields.function): continue if not f.store: @@ -2367,7 +2451,7 @@ class orm(orm_template): fld_def = (field in self._columns) and self._columns[field] or self._inherit_fields[field][2] if fld_def._type in ('many2one', 'one2one'): obj = self.pool.get(fld_def._obj) - if not obj.search(cr, uid, [('id', '=', field_value)]): + if not obj.search(cr, uid, [('id', '=', field_value or False)]): continue if fld_def._type in ('many2many'): obj = self.pool.get(fld_def._obj) @@ -2394,13 +2478,14 @@ class orm(orm_template): field_value = field_value2 value[field] = field_value for key in context or {}: - if key.startswith('default_'): + if key.startswith('default_') and (key[8:] in fields_list): value[key[8:]] = context[key] return value # # Update objects that uses this one to update their _inherits fields # + def _inherits_reload_src(self): for obj in self.pool.obj_pool.values(): if self._name in obj._inherits: @@ -2467,8 +2552,7 @@ class orm(orm_template): fields_to_read = self._columns.keys() # construct a clause for the rules : - d1, d2, tables = self.pool.get('ir.rule').domain_get(cr, user, self._name, context=context) - + d1, d2, tables = self.pool.get('ir.rule').domain_get(cr, user, self._name, 'read', context=context) # all inherited fields + all non inherited fields for which the attribute whose name is in load is True fields_pre = [f for f in fields_to_read if f == self.CONCURRENCY_CHECK_FIELD @@ -2489,17 +2573,16 @@ class orm(orm_template): return '"%s"' % (f,) fields_pre2 = map(convert_field, fields_pre) order_by = self._parent_order or self._order - for i in range(0, len(ids), cr.IN_MAX): - sub_ids = ids[i:i+cr.IN_MAX] + for sub_ids in cr.split_for_in_conditions(ids): if d1: - cr.execute('SELECT %s FROM %s WHERE %s.id = ANY (%%s) AND %s ORDER BY %s' % \ + cr.execute('SELECT %s FROM %s WHERE %s.id IN %%s AND %s ORDER BY %s' % \ (','.join(fields_pre2 + [self._table + '.id']), ','.join(tables), self._table, ' and '.join(d1), order_by),[sub_ids,]+d2) - if not cr.rowcount == len({}.fromkeys(sub_ids)): + if cr.rowcount != len(sub_ids): raise except_orm(_('AccessError'), _('You try to bypass an access rule while reading (Document type: %s).') % self._description) else: - cr.execute('SELECT %s FROM \"%s\" WHERE id = ANY (%%s) ORDER BY %s' % + cr.execute('SELECT %s FROM \"%s\" WHERE id IN %%s ORDER BY %s' % (','.join(fields_pre2 + ['id']), self._table, order_by), (sub_ids,)) res.extend(cr.dictfetchall()) @@ -2529,6 +2612,8 @@ class orm(orm_template): del r['id'] for record in res: + if not record[col]:# if the record is deleted from _inherits table? + continue record.update(res3[record[col]]) if col not in fields_to_read: del record[col] @@ -2536,8 +2621,6 @@ class orm(orm_template): # all fields which need to be post-processed by a simple function (symbol_get) fields_post = filter(lambda x: x in self._columns and self._columns[x]._symbol_get, fields_to_read) if fields_post: - # maybe it would be faster to iterate on the fields then on res, so that we wouldn't need - # to get the _symbol_get in each occurence for r in res: for f in fields_post: r[f] = self._columns[f]._symbol_get(r[f]) @@ -2651,6 +2734,25 @@ class orm(orm_template): if res and res[0]: raise except_orm('ConcurrencyException', _('Records were modified in the meanwhile')) + def check_access_rule(self, cr, uid, ids, operation, context=None): + """Verifies that the operation given by ``operation`` is allowed for the user + according to ir.rules. + @param ``operation``: one of ``'read'``, ``'write'``, ``'unlink'`` + @raise ``except_orm``: if current ir.rules do not permit this operation. + @return: ``None`` if the operation is allowed + """ + where_clause, where_params, tables = self.pool.get('ir.rule').domain_get(cr, uid, self._name, operation, context=context) + if where_clause: + where_clause = ' and ' + ' and '.join(where_clause) + for sub_ids in cr.split_for_in_conditions(ids): + cr.execute('SELECT ' + self._table + '.id FROM ' + ','.join(tables) + + ' WHERE ' + self._table + '.id IN %s' + where_clause, + [sub_ids] + where_params) + if cr.rowcount != len(sub_ids): + raise except_orm(_('AccessError'), + _('Operation prohibited by access rules (Operation: %s, Document type: %s).') + % (operation, self._name)) + def unlink(self, cr, uid, ids, context=None): if not ids: return True @@ -2680,26 +2782,13 @@ class orm(orm_template): # ids2 = [x[self._inherits[key]] for x in res] # self.pool.get(key).unlink(cr, uid, ids2) - d1, d2,tables = self.pool.get('ir.rule').domain_get(cr, uid, self._name, context=context) - if d1: - d1 = ' AND '+' and '.join(d1) - - for i in range(0, len(ids), cr.IN_MAX): - sub_ids = ids[i:i+cr.IN_MAX] - str_d = string.join(('%s',)*len(sub_ids), ',') - if d1: - cr.execute('SELECT '+self._table+'.id FROM '+','.join(tables)+' ' \ - 'WHERE '+self._table+'.id IN ('+str_d+')'+d1, sub_ids+d2) - if not cr.rowcount == len(sub_ids): - raise except_orm(_('AccessError'), - _('You try to bypass an access rule (Document type: %s).') % \ - self._description) - - cr.execute('delete from '+self._table+' ' \ - 'where id in ('+str_d+')', sub_ids) + self.check_access_rule(cr, uid, ids, 'unlink', context=context) + for sub_ids in cr.split_for_in_conditions(ids): + cr.execute('delete from ' + self._table + ' ' \ + 'where id in %s', sub_ids) for order, object, store_ids, fields in result_store: - if object<>self._name: + if object != self._name: obj = self.pool.get(object) cr.execute('select id from '+obj._table+' where id in ('+','.join(map(str, store_ids))+')') rids = map(lambda x: x[0], cr.fetchall()) @@ -2795,29 +2884,10 @@ class orm(orm_template): upd1.append(user) if len(upd0): - - d1, d2,tables = self.pool.get('ir.rule').domain_get(cr, user, self._name, context=context) - if d1: - d1 = ' and '+' and '.join(d1) - - for i in range(0, len(ids), cr.IN_MAX): - sub_ids = ids[i:i+cr.IN_MAX] - ids_str = string.join(map(str, sub_ids), ',') - if d1: - cr.execute('SELECT '+self._table+'.id FROM '+','.join(tables)+' ' \ - 'WHERE '+self._table+'.id IN ('+ids_str+')'+d1, d2) - if not cr.rowcount == len({}.fromkeys(sub_ids)): - raise except_orm(_('AccessError'), - _('You try to bypass an access rule while writing (Document type: %s).') % \ - self._description) - else: - cr.execute('SELECT id FROM "'+self._table+'" WHERE id IN ('+ids_str+')') - if not cr.rowcount == len({}.fromkeys(sub_ids)): - raise except_orm(_('AccessError'), - _('You try to write on an record that doesn\'t exist ' \ - '(Document type: %s).') % self._description) - cr.execute('update '+self._table+' set '+string.join(upd0, ',')+' ' \ - 'where id in ('+ids_str+')', upd1) + self.check_access_rule(cr, user, ids, 'write', context=context) + for sub_ids in cr.split_for_in_conditions(ids): + cr.execute('update ' + self._table + ' set ' + ','.join(upd0) + ' ' \ + 'where id in %s', upd1 + [sub_ids]) if totranslate: # TODO: optimize @@ -2848,11 +2918,9 @@ class orm(orm_template): for table in self._inherits: col = self._inherits[table] nids = [] - for i in range(0, len(ids), cr.IN_MAX): - sub_ids = ids[i:i+cr.IN_MAX] - ids_str = string.join(map(str, sub_ids), ',') + for sub_ids in cr.split_for_in_conditions(ids): cr.execute('select distinct "'+col+'" from "'+self._table+'" ' \ - 'where id in ('+ids_str+')', upd1) + 'where id in %s', (sub_ids,)) nids.extend([x[0] for x in cr.fetchall()]) v = {} @@ -2955,7 +3023,7 @@ class orm(orm_template): if self._inherits[v] not in vals: tocreate[v] = {} else: - tocreate[v] = {self._inherits[v]:vals[self._inherits[v]]} + tocreate[v] = {'id' : vals[self._inherits[v]]} (upd0, upd1, upd2) = ('', '', []) upd_todo = [] for v in vals.keys(): @@ -2979,10 +3047,17 @@ class orm(orm_template): for table in tocreate: if self._inherits[table] in vals: del vals[self._inherits[table]] - id = self.pool.get(table).create(cr, user, tocreate[table]) + + record_id = tocreate[table].pop('id', None) + + if record_id is None: + record_id = self.pool.get(table).create(cr, user, tocreate[table], context=context) + else: + self.pool.get(table).write(cr, user, [record_id], tocreate[table], context=context) + upd0 += ','+self._inherits[table] upd1 += ',%s' - upd2.append(id) + upd2.append(record_id) #Start : Set bool fields to be False if they are not touched(to make search more powerful) bool_fields = [x for x in self._columns.keys() if self._columns[x]._type=='boolean'] @@ -3024,7 +3099,8 @@ class orm(orm_template): upd1 = upd1 + ',' + self._columns[field]._symbol_set[0] upd2.append(self._columns[field]._symbol_set[1](vals[field])) else: - upd_todo.append(field) + if not isinstance(self._columns[field], fields.related): + upd_todo.append(field) if field in self._columns \ and hasattr(self._columns[field], 'selection') \ and vals[field]: @@ -3048,6 +3124,15 @@ class orm(orm_template): upd1 += ',%s,now()' upd2.append(user) cr.execute('insert into "'+self._table+'" (id'+upd0+") values ("+str(id_new)+upd1+')', tuple(upd2)) + d1, d2, tables = self.pool.get('ir.rule').domain_get(cr, user, self._name, 'create', context=context) + if d1: + d1 = ' AND '+' AND '.join(d1) + cr.execute('SELECT '+self._table+'.id FROM '+','.join(tables)+' ' \ + 'WHERE '+self._table+'.id = ' +str(id_new)+d1,d2) + if not cr.rowcount: + raise except_orm(_('AccessError'), + _('You try to bypass an access rule to create (Document type: %s).') \ + % self._name) upd_todo.sort(lambda x, y: self._columns[x].priority-self._columns[y].priority) if self._parent_store: @@ -3185,8 +3270,9 @@ class orm(orm_template): upd0.append('"'+v+'"='+self._columns[v]._symbol_set[0]) upd1.append(self._columns[v]._symbol_set[1](value[v])) upd1.append(id) - cr.execute('update "' + self._table + '" set ' + \ - string.join(upd0, ',') + ' where id = %s', upd1) + if upd0 and upd1: + cr.execute('update "' + self._table + '" set ' + \ + string.join(upd0, ',') + ' where id = %s', upd1) else: for f in val: @@ -3254,7 +3340,7 @@ class orm(orm_template): context = {} # compute the where, order by, limit and offset clauses (qu1, qu2, tables) = self._where_calc(cr, user, args, context=context) - dom = self.pool.get('ir.rule').domain_get(cr, user, self._name, context=context) + dom = self.pool.get('ir.rule').domain_get(cr, user, self._name, 'read', context=context) qu1 = qu1 + dom[0] qu2 = qu2 + dom[1] for t in dom[2]: