diff --git a/bin/netsvc.py b/bin/netsvc.py index df47a56a853..c8e1f0d3b36 100644 --- a/bin/netsvc.py +++ b/bin/netsvc.py @@ -31,56 +31,62 @@ import time import threading -import SimpleXMLRPCServer,signal,sys,xmlrpclib +import SimpleXMLRPCServer, signal, sys, xmlrpclib import SocketServer import socket import logging import os -_service={} -_group={} -_res_id=1 -_res={} +_service = {} +_group = {} +_res_id = 1 +_res = {} + class ServiceEndPointCall(object): - def __init__(self,id,method): - self._id=id - self._meth=method - def __call__(self,*args): - _res[self._id]=self._meth(*args) + def __init__(self, id, method): + self._id = id + self._meth = method + + def __call__(self, *args): + _res[self._id] = self._meth(*args) return self._id + class ServiceEndPoint(object): def __init__(self, name, id): self._id = id - self._meth={} - s=_service[name] + self._meth = {} + s = _service[name] for m in s._method: - self._meth[m]=s._method[m] + self._meth[m] = s._method[m] + def __getattr__(self, name): return ServiceEndPointCall(self._id, self._meth[name]) + class Service(object): _serviceEndPointID = 0 - def __init__(self, name, audience=''): - _service[name]=self - self.__name=name - self._method={} - self.exportedMethods=None - self._response_process=None - self._response_process_id=None - self._response=None - def joinGroup(self,name): + def __init__(self, name, audience=''): + _service[name] = self + self.__name = name + self._method = {} + self.exportedMethods = None + self._response_process = None + self._response_process_id = None + self._response = None + + def joinGroup(self, name): if not name in _group: - _group[name]={} - _group[name][self.__name]=self + _group[name] = {} + _group[name][self.__name] = self def exportMethod(self, m): if callable(m): - self._method[m.__name__]=m + self._method[m.__name__] = m - def serviceEndPoint(self,s): + def serviceEndPoint(self, s): if Service._serviceEndPointID >= 2**16: Service._serviceEndPointID = 0 Service._serviceEndPointID += 1 @@ -89,57 +95,62 @@ class Service(object): def conversationId(self): return 1 - def processResponse(self,s,id): + def processResponse(self, s, id): self._response_process, self._response_process_id = s, id - def processFailure(self,s,id): + def processFailure(self, s, id): pass - def resumeResponse(self,s): + def resumeResponse(self, s): pass - def cancelResponse(self,s): + def cancelResponse(self, s): pass - def suspendResponse(self,s): + def suspendResponse(self, s): if self._response_process: self._response_process(self._response_process_id, _res[self._response_process_id]) - self._response_process=None - self._response=s(self._response_process_id) + self._response_process = None + self._response = s(self._response_process_id) def abortResponse(self, error, description, origin, details): import tools if not tools.config['debug_mode']: - raise Exception("%s -- %s\n\n%s"%(origin,description,details)) + raise Exception("%s -- %s\n\n%s"%(origin, description, details)) else: raise - def currentFailure(self,s): + def currentFailure(self, s): pass + class LocalService(Service): def __init__(self, name): - self.__name=name - s=_service[name] - self._service=s + self.__name = name + s = _service[name] + self._service = s for m in s._method: - setattr(self,m,s._method[m]) + setattr(self, m, s._method[m]) + class ServiceUnavailable(Exception): pass + def service_exist(name): return (name in _service) and bool(_service[name]) + def get_rpc_paths(): return map(lambda s: '/xmlrpc/%s' % s, _service) -LOG_DEBUG='debug' -LOG_INFO='info' -LOG_WARNING='warn' -LOG_ERROR='error' -LOG_CRITICAL='critical' +LOG_DEBUG = 'debug' +LOG_INFO = 'info' +LOG_WARNING = 'warn' +LOG_ERROR = 'error' +LOG_CRITICAL = 'critical' + def init_logger(): from tools import config @@ -175,9 +186,10 @@ def init_logger(): class Logger(object): - def notifyChannel(self,name,level,msg): + def notifyChannel(self, name, level, msg): log = logging.getLogger(name) - getattr(log,level)(msg) + getattr(log, level)(msg) + class Agent(object): _timers = [] @@ -185,9 +197,9 @@ class Agent(object): def setAlarm(self, fn, dt, args=None, kwargs=None): if not args: - args=[] + args = [] if not kwargs: - kwargs={} + kwargs = {} wait = dt - time.time() if wait > 0: self._logger.notifyChannel('timers', LOG_DEBUG, "Job scheduled in %s seconds for %s.%s" % (wait, fn.im_class.__name__, fn.func_name)) @@ -201,42 +213,48 @@ class Agent(object): def quit(cls): for timer in cls._timers: timer.cancel() - quit=classmethod(quit) + quit = classmethod(quit) + class RpcGateway(object): def __init__(self, name): - self.name=name + self.name = name + class Dispatcher(object): def __init__(self): pass - def monitor(self,signal): + + def monitor(self, signal): pass + def run(self): pass + class xmlrpc(object): class RpcGateway(object): def __init__(self, name): - self.name=name + self.name = name + class GenericXMLRPCRequestHandler: def _dispatch(self, method, params): import traceback try: - n=self.path.split("/")[-1] - s=LocalService(n) - m=getattr(s,method) - s._service._response=None - r=m(*params) - res=s._service._response - if res!=None: - r=res + n = self.path.split("/")[-1] + s = LocalService(n) + m = getattr(s, method) + s._service._response = None + r = m(*params) + res = s._service._response + if res != None: + r = res return r except Exception, e: tb_s = reduce(lambda x, y: x+y, traceback.format_exception( sys.exc_type, sys.exc_value, sys.exc_traceback)) - s=str(e) + s = str(e) import tools if tools.config['debug_mode']: import pdb @@ -244,10 +262,12 @@ class GenericXMLRPCRequestHandler: pdb.post_mortem(tb) raise xmlrpclib.Fault(s, tb_s) + class SimpleXMLRPCRequestHandler(GenericXMLRPCRequestHandler, SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.rpc_paths = get_rpc_paths() + class SimpleThreadedXMLRPCServer(SocketServer.ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServer): @@ -256,12 +276,13 @@ class SimpleThreadedXMLRPCServer(SocketServer.ThreadingMixIn, socket.SO_REUSEADDR, 1) SimpleXMLRPCServer.SimpleXMLRPCServer.server_bind(self) + class HttpDaemon(threading.Thread): - def __init__(self, interface,port, secure=False): + def __init__(self, interface, port, secure=False): threading.Thread.__init__(self) - self.__port=port - self.__interface=interface + self.__port = port + self.__interface = interface self.secure = secure if secure: from ssl import SecureXMLRPCServer @@ -277,17 +298,17 @@ class HttpDaemon(threading.Thread): SecureXMLRPCServer.SecureXMLRPCServer.server_bind(self) self.server = SecureThreadedXMLRPCServer((interface, port), - SecureXMLRPCRequestHandler,0) + SecureXMLRPCRequestHandler, 0) else: self.server = SimpleThreadedXMLRPCServer((interface, port), - SimpleXMLRPCRequestHandler,0) + SimpleXMLRPCRequestHandler, 0) - def attach(self,path,gw): + def attach(self, path, gw): pass def stop(self): self.running = False - if os.name <> 'nt': + if os.name != 'nt': if hasattr(socket, 'SHUT_RDWR'): if self.secure: self.server.socket.sock_shutdown(socket.SHUT_RDWR) @@ -342,17 +363,17 @@ class TinySocketClientThread(threading.Thread): self.threads.remove(self) return False try: - s=LocalService(msg[0]) - m=getattr(s,msg[1]) - s._service._response=None - r=m(*msg[2:]) - res=s._service._response - if res!=None: - r=res + s = LocalService(msg[0]) + m = getattr(s, msg[1]) + s._service._response = None + r = m(*msg[2:]) + res = s._service._response + if res != None: + r = res ts.mysend(r) except Exception, e: tb_s = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) - s=str(e) + s = str(e) import tools if tools.config['debug_mode']: import pdb @@ -368,11 +389,12 @@ class TinySocketClientThread(threading.Thread): def stop(self): self.running = False + class TinySocketServerThread(threading.Thread): def __init__(self, interface, port, secure=False): threading.Thread.__init__(self) - self.__port=port - self.__interface=interface + self.__port = port + self.__interface = interface self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.socket.bind((self.__interface, self.__port)) @@ -394,7 +416,7 @@ class TinySocketServerThread(threading.Thread): return False def stop(self): - self.running=False + self.running = False for t in self.threads: t.stop() try: diff --git a/bin/osv/expression.py b/bin/osv/expression.py index 63c6918e04c..050322a5e14 100644 --- a/bin/osv/expression.py +++ b/bin/osv/expression.py @@ -3,6 +3,7 @@ from tools import flatten + class expression(object): """ parse a domain expression @@ -47,7 +48,7 @@ class expression(object): def _is_operator(self, element): return isinstance(element, str) \ - and element in ['&','|'] + and element in ['&', '|'] def _is_leaf(self, element): return isinstance(element, tuple) \ @@ -103,7 +104,7 @@ class expression(object): if isinstance(self.__right, list): self.__right = tuple(self.__right) elif exp and not self._is_expression(self.__exp): - raise ValueError, 'Bad expression: %r' % (self.__exp,) + raise ValueError('Bad expression: %r' % (self.__exp,)) def parse(self, cr, uid, table, context): @@ -162,7 +163,7 @@ class expression(object): self.__left, self.__right = None, None self.__operator = '&' - self.__exp = ('&',) + tuple( [tuple(e.__exp) for e in self.__children] ) + self.__exp = ('&',) + tuple([tuple(e.__exp) for e in self.__children]) elif field._type == 'one2many': if isinstance(self.__right, basestring): @@ -202,7 +203,7 @@ class expression(object): ids2 = list(self.__right) self.__operator = 'in' - if field._obj <> self.__table._name: + if field._obj != self.__table._name: self.__right = ids2 + _rec_get(ids2, field_obj, self.__table._parent_name) else: self.__right = ids2 + _rec_get(ids2, self.__table, self.__left) @@ -264,7 +265,7 @@ class expression(object): del params[i] len_after = len(params) - check_nulls = len_after <> len_before + check_nulls = len_after != len_before query = '(1=0)' if len_after: @@ -323,14 +324,14 @@ class expression(object): else: children = [child.to_sql() for child in self.__children] params = flatten([child[1] for child in children]) - query = "( %s )" % (" %s " % {'&' : 'AND', '|' : 'OR' }[self.__operator]).join([child[0] for child in children if child[0]]) + query = "( %s )" % (" %s " % {'&': 'AND', '|': 'OR'}[self.__operator]).join([child[0] for child in children if child[0]]) return (query, params) def __get_tables(self): return self.__tables + [child.__get_tables() for child in self.__children] def get_tables(self): - return [ '"%s"' % t for t in set(flatten(self.__get_tables()))] + return ['"%s"' % t for t in set(flatten(self.__get_tables()))] #def diff --git a/bin/osv/fields.py b/bin/osv/fields.py index 6983fa2292b..f0de6e97bf1 100644 --- a/bin/osv/fields.py +++ b/bin/osv/fields.py @@ -49,13 +49,15 @@ import warnings import tools + def _symbol_set(symb): - if symb==None or symb==False: + if symb == None or symb == False: return None elif isinstance(symb, unicode): return symb.encode('utf-8') return str(symb) + class _column(object): _classic_read = True _classic_write = True @@ -79,13 +81,13 @@ class _column(object): self.ondelete = ondelete self.translate = translate self._domain = domain or [] - self.relate =False + self.relate = False self._context = context self.group_name = False self.write = False self.read = False self.view_load = 0 - self.select=select + self.select = select for a in args: if args[a]: setattr(self, a, args[a]) @@ -96,21 +98,25 @@ class _column(object): pass def set(self, cr, obj, id, name, value, user=None, context=None): - cr.execute('update '+obj._table+' set '+name+'='+self._symbol_set[0]+' where id=%d', (self._symbol_set[1](value),id) ) + cr.execute('update '+obj._table+' set '+name+'='+self._symbol_set[0]+' where id=%d', (self._symbol_set[1](value), id)) + def set_memory(self, cr, obj, id, name, value, user=None, context=None): - raise Exception, _('Not implemented set_memory method !') + raise Exception(_('Not implemented set_memory method !')) def get_memory(self, cr, obj, ids, name, context=None, values=None): - raise Exception, _('Not implemented get_memory method !') + raise Exception(_('Not implemented get_memory method !')) + def get(self, cr, obj, ids, name, context=None, values=None): - raise Exception, _('undefined get method !') + raise Exception(_('undefined get method !')) def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None): - ids = obj.search(cr, uid, args+self._domain+[(name,'ilike',value)], offset, limit) + ids = obj.search(cr, uid, args+self._domain+[(name, 'ilike', value)], offset, limit) res = obj.read(cr, uid, ids, [name]) return [x[name] for x in res] + def search_memory(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, context=None): - raise Exception, _('Not implemented search_memory method !') + raise Exception(_('Not implemented search_memory method !')) + # --------------------------------------------------------- # Simple fields @@ -121,34 +127,38 @@ class boolean(_column): _symbol_f = lambda x: x and 'True' or 'False' _symbol_set = (_symbol_c, _symbol_f) + class integer(_column): _type = 'integer' _symbol_c = '%d' _symbol_f = lambda x: int(x or 0) _symbol_set = (_symbol_c, _symbol_f) + class reference(_column): _type = 'reference' + def __init__(self, string, selection, size, **args): _column.__init__(self, string=string, size=size, selection=selection, **args) + class char(_column): _type = 'char' - + def __init__(self, string, size, **args): _column.__init__(self, string=string, size=size, **args) self._symbol_set = (self._symbol_c, self._symbol_set_char) - + # takes a string (encoded in utf8) and returns a string (encoded in utf8) def _symbol_set_char(self, symb): - #TODO: - # * we need to remove the "symb==False" from the next line BUT + #TODO: + # * we need to remove the "symb==False" from the next line BUT # for now too many things rely on this broken behavior # * the symb==None test should be common to all data types - if symb==None or symb==False: + if symb == None or symb == False: return None - # we need to convert the string to a unicode object to be able + # we need to convert the string to a unicode object to be able # to evaluate its length (and possibly truncate it) reliably if isinstance(symb, str): u_symb = unicode(symb, 'utf8') @@ -158,38 +168,46 @@ class char(_column): u_symb = unicode(symb) return u_symb.encode('utf8')[:self.size] + class text(_column): _type = 'text' import __builtin__ + class float(_column): _type = 'float' _symbol_c = '%f' _symbol_f = lambda x: __builtin__.float(x or 0.0) _symbol_set = (_symbol_c, _symbol_f) + def __init__(self, string='unknown', digits=None, **args): _column.__init__(self, string=string, **args) self.digits = digits + class date(_column): _type = 'date' + class datetime(_column): _type = 'datetime' + class time(_column): _type = 'time' + class binary(_column): _type = 'binary' _symbol_c = '%s' _symbol_f = lambda symb: symb and psycopg.Binary(symb) or None _symbol_set = (_symbol_c, _symbol_f) + class selection(_column): _type = 'selection' - + def __init__(self, selection, string='unknown', **args): _column.__init__(self, string=string, **args) self.selection = selection @@ -211,7 +229,7 @@ class one2one(_column): _classic_read = False _classic_write = True _type = 'one2one' - + def __init__(self, obj, string='unknown', **args): warnings.warn("The one2one field doesn't work anymore", DeprecationWarning) _column.__init__(self, string=string, **args) @@ -219,25 +237,26 @@ class one2one(_column): def set(self, cr, obj_src, id, field, act, user=None, context=None): if not context: - context={} + context = {} obj = obj_src.pool.get(self._obj) self._table = obj_src.pool.get(self._obj)._table - if act[0]==0: + if act[0] == 0: id_new = obj.create(cr, user, act[1]) - cr.execute('update '+obj_src._table+' set '+field+'=%d where id=%d', (id_new,id)) + cr.execute('update '+obj_src._table+' set '+field+'=%d where id=%d', (id_new, id)) else: cr.execute('select '+field+' from '+obj_src._table+' where id=%d', (act[0],)) - id =cr.fetchone()[0] - obj.write(cr, user, [id] , act[1], context=context) + id = cr.fetchone()[0] + obj.write(cr, user, [id], act[1], context=context) def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None): - return obj.pool.get(self._obj).search(cr, uid, args+self._domain+[('name','like',value)], offset, limit) + return obj.pool.get(self._obj).search(cr, uid, args+self._domain+[('name', 'like', value)], offset, limit) + class many2one(_column): _classic_read = False _classic_write = True _type = 'many2one' - + def __init__(self, obj, string='unknown', **args): _column.__init__(self, string=string, **args) self._obj = obj @@ -254,9 +273,9 @@ class many2one(_column): def get(self, cr, obj, ids, name, user=None, context=None, values=None): if not context: - context={} + context = {} if not values: - values={} + values = {} res = {} for r in values: res[r['id']] = r[name] @@ -268,10 +287,10 @@ class many2one(_column): try: names = dict(obj.name_get(cr, user, filter(None, res.values()), context)) except except_orm: - names={} + names = {} iids = filter(None, res.values()) - cr.execute('select id,'+obj._rec_name+' from '+obj._table+' where id in ('+','.join(map(str,iids))+')') + cr.execute('select id,'+obj._rec_name+' from '+obj._table+' where id in ('+','.join(map(str, iids))+')') for res22 in cr.fetchall(): names[res22[0]] = res22[1] @@ -284,36 +303,37 @@ class many2one(_column): def set(self, cr, obj_src, id, field, values, user=None, context=None): if not context: - context={} + context = {} obj = obj_src.pool.get(self._obj) self._table = obj_src.pool.get(self._obj)._table if type(values)==type([]): for act in values: - if act[0]==0: + if act[0] == 0: id_new = obj.create(cr, act[2]) - cr.execute('update '+obj_src._table+' set '+field+'=%d where id=%d', (id_new,id)) - elif act[0]==1: + cr.execute('update '+obj_src._table+' set '+field+'=%d where id=%d', (id_new, id)) + elif act[0] == 1: obj.write(cr, [act[1]], act[2], context=context) - elif act[0]==2: + elif act[0] == 2: cr.execute('delete from '+self._table+' where id=%d', (act[1],)) - elif act[0]==3 or act[0]==5: + elif act[0] == 3 or act[0] == 5: cr.execute('update '+obj_src._table+' set '+field+'=null where id=%d', (id,)) - elif act[0]==4: - cr.execute('update '+obj_src._table+' set '+field+'=%d where id=%d', (act[1],id)) + elif act[0] == 4: + cr.execute('update '+obj_src._table+' set '+field+'=%d where id=%d', (act[1], id)) else: if values: - cr.execute('update '+obj_src._table+' set '+field+'=%d where id=%d', (values,id)) + cr.execute('update '+obj_src._table+' set '+field+'=%d where id=%d', (values, id)) else: cr.execute('update '+obj_src._table+' set '+field+'=null where id=%d', (id,)) def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None): - return obj.pool.get(self._obj).search(cr, uid, args+self._domain+[('name','like',value)], offset, limit) + return obj.pool.get(self._obj).search(cr, uid, args+self._domain+[('name', 'like', value)], offset, limit) + class one2many(_column): _classic_read = False _classic_write = False _type = 'one2many' - + def __init__(self, obj, fields_id, string='unknown', limit=None, **args): _column.__init__(self, string=string, **args) self._obj = obj @@ -330,42 +350,41 @@ class one2many(_column): res = {} for id in ids: res[id] = [] - ids2 = obj.pool.get(self._obj).search(cr, user, [(self._fields_id,'in',ids)], limit=self._limit) + ids2 = obj.pool.get(self._obj).search(cr, user, [(self._fields_id, 'in', ids)], limit=self._limit) for r in obj.pool.get(self._obj).read(cr, user, ids2, [self._fields_id], context=context, load='_classic_write'): if r[self._fields_id] in res: - res[r[self._fields_id]].append( r['id'] ) + res[r[self._fields_id]].append(r['id']) return res def set_memory(self, cr, obj, id, field, values, user=None, context=None): if not context: - context={} + context = {} if not values: return obj = obj.pool.get(self._obj) for act in values: - if act[0]==0: + if act[0] == 0: act[2][self._fields_id] = id obj.create(cr, user, act[2], context=context) - elif act[0]==1: - obj.write(cr, user, [act[1]] , act[2], context=context) - elif act[0]==2: + elif act[0] == 1: + obj.write(cr, user, [act[1]], act[2], context=context) + elif act[0] == 2: obj.unlink(cr, user, [act[1]], context=context) - elif act[0]==3: + elif act[0] == 3: obj.datas[act[1]][self._fields_id] = False - elif act[0]==4: + elif act[0] == 4: obj.datas[act[1]] = id - elif act[0]==5: + elif act[0] == 5: for o in obj.datas.values(): - if o[self._fields_id]==id: + if o[self._fields_id] == id: o[self._fields_id] = False - elif act[0]==6: + elif act[0] == 6: for id2 in (act[2] or []): obj.datas[id2][self._fields_id] = id def search_memory(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, operator='like', context=None): raise _('Not Implemented') - def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None): if not context: context = {} @@ -374,33 +393,33 @@ class one2many(_column): res = {} for id in ids: res[id] = [] - ids2 = obj.pool.get(self._obj).search(cr, user, [(self._fields_id,'in',ids)], limit=self._limit) + ids2 = obj.pool.get(self._obj).search(cr, user, [(self._fields_id, 'in', ids)], limit=self._limit) for r in obj.pool.get(self._obj)._read_flat(cr, user, ids2, [self._fields_id], context=context, load='_classic_write'): - res[r[self._fields_id]].append( r['id'] ) + res[r[self._fields_id]].append(r['id']) return res def set(self, cr, obj, id, field, values, user=None, context=None): if not context: - context={} + context = {} if not values: return _table = obj.pool.get(self._obj)._table obj = obj.pool.get(self._obj) for act in values: - if act[0]==0: + if act[0] == 0: act[2][self._fields_id] = id obj.create(cr, user, act[2], context=context) - elif act[0]==1: - obj.write(cr, user, [act[1]] , act[2], context=context) - elif act[0]==2: + elif act[0] == 1: + obj.write(cr, user, [act[1]], act[2], context=context) + elif act[0] == 2: obj.unlink(cr, user, [act[1]], context=context) - elif act[0]==3: + elif act[0] == 3: cr.execute('update '+_table+' set '+self._fields_id+'=null where id=%d', (act[1],)) - elif act[0]==4: - cr.execute('update '+_table+' set '+self._fields_id+'=%d where id=%d', (id,act[1])) - elif act[0]==5: + elif act[0] == 4: + cr.execute('update '+_table+' set '+self._fields_id+'=%d where id=%d', (id, act[1])) + elif act[0] == 5: cr.execute('update '+_table+' set '+self._fields_id+'=null where '+self._fields_id+'=%d', (id,)) - elif act[0]==6: + elif act[0] == 6: if not act[2]: ids2 = [0] else: @@ -408,9 +427,11 @@ class one2many(_column): cr.execute('update '+_table+' set '+self._fields_id+'=NULL where '+self._fields_id+'=%d and id not in ('+','.join(map(str, ids2))+')', (id,)) if act[2]: cr.execute('update '+_table+' set '+self._fields_id+'=%d where id in ('+','.join(map(str, act[2]))+')', (id,)) + def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, operator='like'): return obj.pool.get(self._obj).name_search(cr, uid, value, self._domain, offset, limit) + # # Values: (0, 0, { fields }) create # (1, ID, { fields }) modification @@ -424,7 +445,7 @@ class many2many(_column): _classic_read = False _classic_write = False _type = 'many2many' - + def __init__(self, obj, rel, id1, id2, string='unknown', limit=None, **args): _column.__init__(self, string=string, **args) self._obj = obj @@ -435,15 +456,15 @@ class many2many(_column): def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None): if not context: - context={} + context = {} if not values: - values={} + values = {} res = {} if not ids: return res for id in ids: res[id] = [] - ids_s = ','.join(map(str,ids)) + ids_s = ','.join(map(str, ids)) limit_str = self._limit is not None and ' limit %d' % self._limit or '' obj = obj.pool.get(self._obj) @@ -463,39 +484,39 @@ class many2many(_column): def set(self, cr, obj, id, name, values, user=None, context=None): if not context: - context={} + context = {} if not values: return obj = obj.pool.get(self._obj) for act in values: - if act[0]==0: + if act[0] == 0: idnew = obj.create(cr, user, act[2]) - cr.execute('insert into '+self._rel+' ('+self._id1+','+self._id2+') values (%d,%d)', (id,idnew)) - elif act[0]==1: - obj.write(cr, user, [act[1]] , act[2], context=context) - elif act[0]==2: + cr.execute('insert into '+self._rel+' ('+self._id1+','+self._id2+') values (%d,%d)', (id, idnew)) + elif act[0] == 1: + obj.write(cr, user, [act[1]], act[2], context=context) + elif act[0] == 2: obj.unlink(cr, user, [act[1]], context=context) - elif act[0]==3: - cr.execute('delete from '+self._rel+' where ' + self._id1 + '=%d and '+ self._id2 + '=%d', (id,act[1])) - elif act[0]==4: - cr.execute('insert into '+self._rel+' ('+self._id1+','+self._id2+') values (%d,%d)', (id,act[1])) - elif act[0]==5: + elif act[0] == 3: + cr.execute('delete from '+self._rel+' where ' + self._id1 + '=%d and '+ self._id2 + '=%d', (id, act[1])) + elif act[0] == 4: + cr.execute('insert into '+self._rel+' ('+self._id1+','+self._id2+') values (%d,%d)', (id, act[1])) + elif act[0] == 5: cr.execute('update '+self._rel+' set '+self._id2+'=null where '+self._id2+'=%d', (id,)) - elif act[0]==6: + elif act[0] == 6: - d1, d2 = obj.pool.get('ir.rule').domain_get(cr, user, obj._name) + d1, d2 = obj.pool.get('ir.rule').domain_get(cr, user, obj._name) if d1: d1 = ' and '+d1 - cr.execute('delete from '+self._rel+' where '+self._id1+'=%d AND '+self._id2+' IN (SELECT '+self._rel+'.'+self._id2+' FROM '+self._rel+', '+obj._table+' WHERE '+self._rel+'.'+self._id1+'=%d AND '+self._rel+'.'+self._id2+' = '+obj._table+'.id '+ d1 +')', [id, id]+d2 ) + cr.execute('delete from '+self._rel+' where '+self._id1+'=%d AND '+self._id2+' IN (SELECT '+self._rel+'.'+self._id2+' FROM '+self._rel+', '+obj._table+' WHERE '+self._rel+'.'+self._id1+'=%d AND '+self._rel+'.'+self._id2+' = '+obj._table+'.id '+ d1 +')', [id, id]+d2) - for act_nbr in act[2]: + for act_nbr in act[2]: cr.execute('insert into '+self._rel+' ('+self._id1+','+self._id2+') values (%d, %d)', (id, act_nbr)) # # TODO: use a name_search # def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, operator='like'): - return obj.pool.get(self._obj).search(cr, uid, args+self._domain+[('name',operator,value)], offset, limit) + return obj.pool.get(self._obj).search(cr, uid, args+self._domain+[('name', operator, value)], offset, limit) def get_memory(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None): result = {} @@ -508,32 +529,31 @@ class many2many(_column): return for act in values: # TODO: use constants instead of these magic numbers - if act[0]==0: + if act[0] == 0: raise _('Not Implemented') - elif act[0]==1: + elif act[0] == 1: raise _('Not Implemented') - elif act[0]==2: + elif act[0] == 2: raise _('Not Implemented') - elif act[0]==3: + elif act[0] == 3: raise _('Not Implemented') - elif act[0]==4: + elif act[0] == 4: raise _('Not Implemented') - elif act[0]==5: + elif act[0] == 5: raise _('Not Implemented') - elif act[0]==6: + elif act[0] == 6: obj.datas[id][name] = act[2] # --------------------------------------------------------- # Function fields # --------------------------------------------------------- - class function(_column): _classic_read = False _classic_write = False _type = 'function' _properties = True - + def __init__(self, fnct, arg=None, fnct_inv=None, fnct_inv_arg=None, type='float', fnct_search=None, obj=None, method=False, store=False, **args): _column.__init__(self, **args) self._obj = obj @@ -553,7 +573,7 @@ class function(_column): self._symbol_c = '%f' self._symbol_f = lambda x: __builtin__.float(x or 0.0) self._symbol_set = (self._symbol_c, self._symbol_f) - + def search(self, cr, uid, obj, name, args): if not self._fnct_search: #CHECKME: should raise an exception @@ -562,9 +582,9 @@ class function(_column): def get(self, cr, obj, ids, name, user=None, context=None, values=None): if not context: - context={} + context = {} if not values: - values={} + values = {} res = {} table = obj._table if self._method: @@ -575,10 +595,11 @@ class function(_column): def set(self, cr, obj, id, name, value, user=None, context=None): if not context: - context={} + context = {} if self._fnct_inv: self._fnct_inv(obj, cr, user, id, name, value, self._fnct_inv_arg, context) + # --------------------------------------------------------- # Serialized fields # --------------------------------------------------------- @@ -596,7 +617,7 @@ class property(function): def _fnct_write(self, obj, cr, uid, id, prop, id_val, val, context=None): if not context: - context={} + context = {} (obj_dest,) = val definition_id = self._field_get(cr, uid, obj._name, prop) @@ -629,7 +650,7 @@ class property(function): def _fnct_read(self, obj, cr, uid, ids, prop, val, context=None): if not context: - context={} + context = {} property = obj.pool.get('ir.property') definition_id = self._field_get(cr, uid, obj._name, prop) @@ -646,7 +667,7 @@ class property(function): res = {} for id in ids: - res[id]= default_val + res[id] = default_val for prop in property.browse(cr, uid, nids): res[int(prop.res_id.split(',')[1])] = (prop.value and \ int(prop.value.split(',')[1])) or False diff --git a/bin/osv/orm.py b/bin/osv/orm.py index 0f0ec2f70c7..ffb2fbe16c6 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -49,21 +49,18 @@ import time import types from xml import dom, xpath -from xml.parsers import expat import string -import pooler -import psycopg import netsvc import re import fields -import ir import tools from tools.config import config regex_order = re.compile('^([a-zA-Z0-9_]+( desc)?( asc)?,?)+$', re.I) + def intersect(la, lb): return filter(lambda x: x in lb, la) @@ -74,11 +71,12 @@ class except_orm(Exception): self.value = value self.args = (name, value) + # Readonly python database object browser class browse_null(object): def __init__(self): - self.id=False + self.id = False def __getitem__(self, name): return False @@ -92,6 +90,7 @@ class browse_null(object): def __nonzero__(self): return False + # # TODO: execute an object method on browse_record_list # @@ -125,13 +124,13 @@ class browse_record(object): cache.setdefault(table._name, {}) self._data = cache[table._name] if not id in self._data: - self._data[id] = {'id':id} + self._data[id] = {'id': id} self._cache = cache def __getitem__(self, name): if name == 'id': return self._id - if not self._data[self._id].has_key(name): + if not name in self._data[self._id]: # build the list of fields we will fetch # fetch the definition of the field which was asked for @@ -140,7 +139,7 @@ class browse_record(object): elif name in self._table._inherit_fields: col = self._table._inherit_fields[name][2] elif hasattr(self._table, name): - if isinstance( getattr(self._table, name), (types.MethodType,types.LambdaType,types.FunctionType)): + if isinstance(getattr(self._table, name), (types.MethodType, types.LambdaType, types.FunctionType)): return lambda *args, **argv: getattr(self._table, name)(self._cr, self._uid, [self._id], *args, **argv) else: return getattr(self._table, name) @@ -159,13 +158,13 @@ class browse_record(object): ffields += filter(lambda x: x[1]._classic_write, inherits) # otherwise we fetch only that field else: - ffields = [(name,col)] - ids = filter(lambda id: not self._data[id].has_key(name), self._data.keys()) + ffields = [(name, col)] + ids = filter(lambda id: not name in self._data[id], self._data.keys()) # read the data fffields = map(lambda x: x[0], ffields) datas = self._table.read(self._cr, self._uid, ids, fffields, context=self._context, load="_classic_write") if self._fields_process: - for n,f in ffields: + for n, f in ffields: if f._type in self._fields_process: for d in datas: d[n] = self._fields_process[f._type](d[n]) @@ -174,11 +173,11 @@ class browse_record(object): # create browse records for 'remote' objects for data in datas: - for n,f in ffields: + for n, f in ffields: if f._type in ('many2one', 'one2one'): if data[n]: obj = self._table.pool.get(f._obj) - compids=False + compids = False if not f._classic_write: ids2 = data[n][0] else: @@ -190,7 +189,7 @@ class browse_record(object): else: 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) + 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) return self._data[self._id][name] @@ -234,38 +233,38 @@ def get_pg_type(f): ''' type_dict = { - fields.boolean:'bool', - fields.integer:'int4', - fields.text:'text', - fields.date:'date', - fields.time:'time', - fields.datetime:'timestamp', - fields.binary:'bytea', - fields.many2one:'int4', + fields.boolean: 'bool', + fields.integer: 'int4', + fields.text: 'text', + fields.date: 'date', + fields.time: 'time', + fields.datetime: 'timestamp', + fields.binary: 'bytea', + fields.many2one: 'int4', } - if type_dict.has_key(type(f)): + if type(f) in type_dict: 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(%d,%d)' % (f.digits[0], f.digits[1])) else: f_type = ('float8', 'DOUBLE PRECISION') elif isinstance(f, (fields.char, fields.reference)): f_type = ('varchar', 'VARCHAR(%d)' % (f.size,)) elif isinstance(f, fields.selection): if isinstance(f.selection, list) and isinstance(f.selection[0][0], (str, unicode)): - f_size = reduce(lambda x,y: max(x,len(y[0])), f.selection, f.size or 16) + f_size = reduce(lambda x, y: max(x, len(y[0])), f.selection, f.size or 16) elif isinstance(f.selection, list) and isinstance(f.selection[0][0], int): f_size = -1 else: - f_size = (hasattr(f,'size') and f.size) or 16 + f_size = (hasattr(f, 'size') and f.size) or 16 if f_size == -1: f_type = ('int4', 'INTEGER') else: f_type = ('varchar', 'VARCHAR(%d)' % f_size) - elif isinstance(f, fields.function) and type_dict.has_key(eval('fields.'+(f._type))): - t=eval('fields.'+(f._type)) + elif isinstance(f, fields.function) and eval('fields.'+(f._type)) in type_dict: + t = eval('fields.'+(f._type)) f_type = (type_dict[t], type_dict[t]) elif isinstance(f, fields.function) and f._type == 'float': f_type = ('float8', 'DOUBLE PRECISION') @@ -277,6 +276,7 @@ def get_pg_type(f): f_type = None return f_type + class orm_template(object): _name = None _columns = {} @@ -297,10 +297,10 @@ class orm_template(object): # reference model in order to have a description of its fonctionnality in custom_report cr.execute('SELECT nextval(%s)', ('ir_model_id_seq',)) id = cr.fetchone()[0] - cr.execute("INSERT INTO ir_model (id,model, name, info) VALUES (%s, %s, %s, %s)", (id,self._name, self._description, self.__doc__)) + cr.execute("INSERT INTO ir_model (id,model, name, info) VALUES (%s, %s, %s, %s)", (id, self._name, self._description, self.__doc__)) if 'module' in context: cr.execute("INSERT INTO ir_model_data (name,date_init,date_update,module,model,res_id) VALUES (%s, now(), now(), %s, %s, %s)", \ - ( 'model_'+self._table, context['module'], 'ir.model', id) + ('model_'+self._table, context['module'], 'ir.model', id) ) cr.commit() @@ -312,25 +312,25 @@ class orm_template(object): cr.execute("SELECT id FROM ir_model WHERE model='%s'" % self._name) model_id = cr.fetchone()[0] - for (k,f) in self._columns.items(): + for (k, f) in self._columns.items(): vals = { - 'model_id':model_id, - 'model':self._name, - 'name':k, - 'field_description':f.string.replace("'", " "), - 'ttype':f._type, - 'relate':(f.relate and 1) or 0, - 'relation':f._obj or 'NULL', - 'group_name':f.group_name or '', - 'view_load':(f.view_load and 1) or 0, - 'select_level':str(f.select or 0) + 'model_id': model_id, + 'model': self._name, + 'name': k, + 'field_description': f.string.replace("'", " "), + 'ttype': f._type, + 'relate': (f.relate and 1) or 0, + 'relation': f._obj or 'NULL', + 'group_name': f.group_name or '', + 'view_load': (f.view_load and 1) or 0, + 'select_level': str(f.select or 0) } if k not in cols: cr.execute('select nextval(%s)', ('ir_model_fields_id_seq',)) id = cr.fetchone()[0] vals['id'] = id cr.execute("""INSERT INTO ir_model_fields ( - id, model_id, model, name, field_description, ttype, + id, model_id, model, name, field_description, ttype, relate,relation,group_name,view_load,state,select_level ) VALUES ( %d,%s,%s,%s,%s,%s, %s,%s,%s,%s,%s, %s @@ -341,21 +341,21 @@ class orm_template(object): )) if 'module' in context: cr.execute("INSERT INTO ir_model_data (name,date_init,date_update,module,model,res_id) VALUES (%s, now(), now(), %s, %s, %s)", \ - ( ('field_'+self._table+'_'+k)[:64], context['module'], 'ir.model.fields', id) + (('field_'+self._table+'_'+k)[:64], context['module'], 'ir.model.fields', id) ) else: - for key,val in vals.items(): - if cols[k][key]<>vals[key]: + for key, val in vals.items(): + if cols[k][key] != vals[key]: print 'Different', cols[k][key], vals[key], k, key, self._name - cr.execute('update ir_model_fields set field_description=%s where model=%s and name=%s', (vals['field_description'],vals['model'],vals['name'])) + cr.execute('update ir_model_fields set field_description=%s where model=%s and name=%s', (vals['field_description'], vals['model'], vals['name'])) cr.commit() cr.execute("""UPDATE ir_model_fields SET model_id=%s, field_description=%s, ttype=%s, relate=%s, relation=%s, group_name=%s, view_load=%s, select_level=%s WHERE model=%s AND name=%s""", ( - vals['model_id'],vals['field_description'],vals['ttype'],bool(vals['relate']), - vals['relation'], vals['group_name'], bool(vals['view_load']), + vals['model_id'], vals['field_description'], vals['ttype'], bool(vals['relate']), + vals['relation'], vals['group_name'], bool(vals['view_load']), vals['select_level'], vals['model'], vals['name'] )) continue @@ -368,19 +368,19 @@ class orm_template(object): if not self._description: self._description = self._name if not self._table: - self._table=self._name.replace('.','_') + self._table = self._name.replace('.', '_') def browse(self, cr, uid, select, context=None, list_class=None, fields_process={}): if not context: - context={} + context = {} self._list_class = list_class or browse_record_list cache = {} # need to accepts ints and longs because ids coming from a method # launched by button in the interface have a type long... if isinstance(select, (int, long)): - return browse_record(cr,uid,select,self,cache, context=context, list_class=self._list_class, fields_process=fields_process) - elif isinstance(select,list): - return self._list_class([browse_record(cr,uid,id,self,cache, context=context, list_class=self._list_class, fields_process=fields_process) for id in select], context) + return browse_record(cr, uid, select, self, cache, context=context, list_class=self._list_class, fields_process=fields_process) + elif isinstance(select, list): + return self._list_class([browse_record(cr, uid, id, self, cache, context=context, list_class=self._list_class, fields_process=fields_process) for id in select], context) else: return browse_null() @@ -393,7 +393,7 @@ class orm_template(object): if f: r = row i = 0 - while i=len(line): - raise Exception, _('Please check that all your lines have %d columns.') % (len(fields),) + if i >= len(line): + raise Exception(_('Please check that all your lines have %d columns.') % (len(fields),)) field = fields[i] if field == ["id"]: - data_id= line[i] + data_id = line[i] continue if (len(field)==len(prefix)+1) and field[len(prefix)].endswith(':id'): res_id = False @@ -471,7 +472,7 @@ class orm_template(object): if res_id2: res_id.append(res_id2) if len(res_id): - res_id=[(6,0,res_id)] + res_id = [(6, 0, res_id)] else: if '.' in line[i]: module, xml_id = line[i].rsplit('.', 1) @@ -491,9 +492,9 @@ class orm_template(object): if (len(field) == len(prefix)+1) and \ (prefix == field[0:len(prefix)]): if fields_def[field[len(prefix)]]['type'] == 'integer': - res =line[i] and int(line[i]) + res = line[i] and int(line[i]) elif fields_def[field[len(prefix)]]['type'] == 'float': - res =line[i] and float(line[i]) + res = line[i] and float(line[i]) elif fields_def[field[len(prefix)]]['type'] == 'selection': res = False if isinstance(fields_def[field[len(prefix)]]['selection'], @@ -540,7 +541,7 @@ class orm_template(object): else: res.append(res3) if len(res): - res= [(6,0,res)] + res = [(6, 0, res)] else: res = line[i] or False row[field[len(prefix)]] = res @@ -558,9 +559,9 @@ class orm_template(object): (newrow, max2, w2, translate2, data_id2) = res nbrmax = max(nbrmax, max2) warning = warning + w2 - reduce(lambda x,y: x and y, newrow) + reduce(lambda x, y: x and y, newrow) row[field] = (reduce(lambda x, y: x or y, newrow.values()) and \ - [(0,0,newrow)]) or [] + [(0, 0, newrow)]) or [] i = max2 while (position+i) 'ir_actions': + if ref != 'ir_actions': cr.execute("ALTER TABLE \"%s\" ADD FOREIGN KEY (\"%s\") REFERENCES \"%s\" ON DELETE %s" % (self._table, k, ref, f.ondelete)) if f.select: cr.execute("CREATE INDEX \"%s_%s_index\" ON \"%s\" (\"%s\")" % (self._table, k, self._table, k)) @@ -1388,15 +1391,15 @@ class orm(orm_template): # We update varchar size, otherwise, we keep DB size # to avoid truncated string... if f_pg_size < f.size: - cr.execute("ALTER TABLE \"%s\" RENAME COLUMN \"%s\" TO temp_change_size" % (self._table,k)) - cr.execute("ALTER TABLE \"%s\" ADD COLUMN \"%s\" VARCHAR(%d)" % (self._table,k,f.size)) - cr.execute("UPDATE \"%s\" SET \"%s\"=temp_change_size::VARCHAR(%d)" % (self._table,k,f.size)) + cr.execute("ALTER TABLE \"%s\" RENAME COLUMN \"%s\" TO temp_change_size" % (self._table, k)) + cr.execute("ALTER TABLE \"%s\" ADD COLUMN \"%s\" VARCHAR(%d)" % (self._table, k, f.size)) + cr.execute("UPDATE \"%s\" SET \"%s\"=temp_change_size::VARCHAR(%d)" % (self._table, k, f.size)) cr.execute("ALTER TABLE \"%s\" DROP COLUMN temp_change_size" % (self._table,)) cr.commit() # if the field is required and hasn't got a NOT NULL constraint if f.required and f_pg_notnull == 0: # set the field to the default value if any - if self._defaults.has_key(k): + if k in self._defaults: default = self._defaults[k](self, cr, 1, {}) if not (default is False): cr.execute("UPDATE \"%s\" SET \"%s\"='%s' WHERE %s is NULL" % (self._table, k, default, k)) @@ -1409,7 +1412,7 @@ class orm(orm_template): logger.notifyChannel('init', netsvc.LOG_WARNING, 'unable to set a NOT NULL constraint on column %s of the %s table !\nIf you want to have it, you should update the records and execute manually:\nALTER TABLE %s ALTER COLUMN %s SET NOT NULL' % (k, self._table, self._table, k)) cr.commit() elif not f.required and f_pg_notnull == 1: - cr.execute("ALTER TABLE \"%s\" ALTER COLUMN \"%s\" DROP NOT NULL" % (self._table,k)) + cr.execute("ALTER TABLE \"%s\" ALTER COLUMN \"%s\" DROP NOT NULL" % (self._table, k)) cr.commit() cr.execute("SELECT indexname FROM pg_indexes WHERE indexname = '%s_%s_index' and tablename = '%s'" % (self._table, k, self._table)) res = cr.dictfetchall() @@ -1456,26 +1459,26 @@ class orm(orm_template): cr.execute("SELECT relname FROM pg_class WHERE relkind in ('r','v') AND relname='%s'" % self._table) create = not bool(cr.fetchone()) - for (key,con,_) in self._sql_constraints: + for (key, con, _) in self._sql_constraints: cr.execute("SELECT conname FROM pg_constraint where conname='%s_%s'" % (self._table, key)) if not cr.dictfetchall(): try: - cr.execute('alter table \"%s\" add constraint \"%s_%s\" %s' % (self._table,self._table,key, con,)) + cr.execute('alter table \"%s\" add constraint \"%s_%s\" %s' % (self._table, self._table, key, con,)) cr.commit() except: - logger.notifyChannel('init', netsvc.LOG_WARNING, 'unable to add \'%s\' constraint on table %s !\n If you want to have it, you should update the records and execute manually:\nALTER table %s ADD CONSTRAINT %s_%s %s' % (con, self._table, self._table,self._table,key, con,)) + logger.notifyChannel('init', netsvc.LOG_WARNING, 'unable to add \'%s\' constraint on table %s !\n If you want to have it, you should update the records and execute manually:\nALTER table %s ADD CONSTRAINT %s_%s %s' % (con, self._table, self._table, self._table, key, con,)) if create: - if hasattr(self,"_sql"): + if hasattr(self, "_sql"): for line in self._sql.split(';'): - line2 = line.replace('\n','').strip() + line2 = line.replace('\n', '').strip() if line2: cr.execute(line2) cr.commit() def __init__(self, cr): super(orm, self).__init__(cr) - f=filter(lambda a: isinstance(self._columns[a], fields.function) and self._columns[a].store, self._columns) + f = filter(lambda a: isinstance(self._columns[a], fields.function) and self._columns[a].store, self._columns) if f: list_store = [] tuple_store = () @@ -1484,29 +1487,29 @@ class orm(orm_template): if not self._columns[store_field].store == True: dict_store = self._columns[store_field].store key = dict_store.keys() - list_data=[] + list_data = [] for i in key: - tuple_store=self._name,store_field,self._columns[store_field]._fnct.__name__,tuple(dict_store[i][0]),dict_store[i][1],i + tuple_store = self._name, store_field, self._columns[store_field]._fnct.__name__, tuple(dict_store[i][0]), dict_store[i][1], i list_data.append(tuple_store) #tuple_store=self._name,store_field,self._columns[store_field]._fnct.__name__,tuple(dict_store[key[0]][0]),dict_store[key[0]][1] for l in list_data: - list_store=[] + list_store = [] if l[5] in self.pool._store_function.keys(): self.pool._store_function[l[5]].append(l) temp_list = list(set(self.pool._store_function[l[5]])) - self.pool._store_function[l[5]]=temp_list + self.pool._store_function[l[5]] = temp_list else: list_store.append(l) - self.pool._store_function[l[5]]=list_store + self.pool._store_function[l[5]] = list_store - for (key,_,msg) in self._sql_constraints: + for (key, _, msg) in self._sql_constraints: self.pool._sql_error[self._table+'_'+key] = msg # Load manual fields - cr.execute("SELECT id FROM ir_model_fields WHERE name=%s AND model=%s", ('state','ir.model.fields')) + cr.execute("SELECT id FROM ir_model_fields WHERE name=%s AND model=%s", ('state', 'ir.model.fields')) if cr.fetchone(): - cr.execute('SELECT * FROM ir_model_fields WHERE model=%s AND state=%s', (self._name,'manual')) + cr.execute('SELECT * FROM ir_model_fields WHERE model=%s AND state=%s', (self._name, 'manual')) for field in cr.dictfetchall(): if field['name'] in self._columns: continue @@ -1539,7 +1542,7 @@ class orm(orm_template): def default_get(self, cr, uid, fields_list, context=None): if not context: - context={} + context = {} value = {} # get the default values for the inherited fields for t in self._inherits.keys(): @@ -1551,7 +1554,7 @@ class orm(orm_template): if f in self._defaults: value[f] = self._defaults[f](self, cr, uid, context) fld_def = ((f in self._columns) and self._columns[f]) \ - or ((f in self._inherit_fields ) and self._inherit_fields[f][2]) \ + or ((f in self._inherit_fields) and self._inherit_fields[f][2]) \ or False if isinstance(fld_def, fields.property): property_obj = self.pool.get('ir.property') @@ -1615,51 +1618,51 @@ class orm(orm_template): for table in self._inherits: res.update(self.pool.get(table)._inherit_fields) for col in self.pool.get(table)._columns.keys(): - res[col]=(table, self._inherits[table], self.pool.get(table)._columns[col]) + res[col] = (table, self._inherits[table], self.pool.get(table)._columns[col]) for col in self.pool.get(table)._inherit_fields.keys(): - res[col]=(table, self._inherits[table], self.pool.get(table)._inherit_fields[col][2]) - self._inherit_fields=res + res[col] = (table, self._inherits[table], self.pool.get(table)._inherit_fields[col][2]) + self._inherit_fields = res self._inherits_reload_src() def fields_get(self, cr, user, fields=None, context=None): - read_access= self.pool.get('ir.model.access').check(cr, user, self._name, 'write', raise_exception=False) + read_access = self.pool.get('ir.model.access').check(cr, user, self._name, 'write', raise_exception=False) return super(orm, self).fields_get(cr, user, fields, context, read_access) def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'): if not context: - context={} + context = {} self.pool.get('ir.model.access').check(cr, user, self._name, 'read') if not fields: fields = self._columns.keys() + self._inherit_fields.keys() select = ids if isinstance(ids, (int, long)): select = [ids] - result = self._read_flat(cr, user, select, fields, context, load) + result = self._read_flat(cr, user, select, fields, context, load) for r in result: - for key,v in r.items(): + for key, v in r.items(): if v == None: - r[key]=False + r[key] = False if isinstance(ids, (int, long)): return result[0] return result def _read_flat(self, cr, user, ids, fields, context=None, load='_classic_read'): if not context: - context={} + context = {} if not ids: return [] - if fields==None: + if fields == None: fields = self._columns.keys() # construct a clause for the rules : d1, d2 = self.pool.get('ir.rule').domain_get(cr, user, self._name) # all inherited fields + all non inherited fields for which the attribute whose name is in load is True - fields_pre = filter(lambda x: x in self._columns and getattr(self._columns[x],'_classic_write'), fields) + self._inherits.values() + fields_pre = filter(lambda x: x in self._columns and getattr(self._columns[x], '_classic_write'), fields) + self._inherits.values() res = [] - if len(fields_pre) : + if len(fields_pre): fields_pre2 = map(lambda x: (x in ('create_date', 'write_date')) and ('date_trunc(\'second\', '+x+') as '+x) or '"'+x+'"', fields_pre) for i in range(0, len(ids), cr.IN_MAX): sub_ids = ids[i:i+cr.IN_MAX] @@ -1667,7 +1670,7 @@ class orm(orm_template): cr.execute('SELECT %s FROM \"%s\" WHERE id IN (%s) AND %s ORDER BY %s' % \ (','.join(fields_pre2 + ['id']), self._table, ','.join([str(x) for x in sub_ids]), d1, - self._order),d2) + self._order), d2) if not cr.rowcount == len({}.fromkeys(sub_ids)): raise except_orm(_('AccessError'), _('You try to bypass an access rule (Document type: %s).') % self._description) @@ -1761,22 +1764,22 @@ class orm(orm_template): def perm_read(self, cr, user, ids, context=None, details=True): if not context: - context={} + context = {} if not ids: return [] fields = '' if self._log_access: - fields =', u.create_uid, u.create_date, u.write_uid, u.write_date' + fields = ', u.create_uid, u.create_date, u.write_uid, u.write_date' if isinstance(ids, (int, long)): ids_str = str(ids) else: - ids_str = string.join(map(lambda x:str(x), ids),',') + ids_str = string.join(map(lambda x: str(x), ids), ',') cr.execute('select u.id'+fields+' from "'+self._table+'" u where u.id in ('+ids_str+')') res = cr.dictfetchall() for r in res: for key in r: r[key] = r[key] or False - if key in ('write_uid','create_uid','uid') and details: + if key in ('write_uid', 'create_uid', 'uid') and details: if r[key]: r[key] = self.pool.get('res.users').name_get(cr, user, [r[key]])[0] if isinstance(ids, (int, long)): @@ -1785,7 +1788,7 @@ class orm(orm_template): def unlink(self, cr, uid, ids, context=None): if not context: - context={} + context = {} if not ids: return True if isinstance(ids, (int, long)): @@ -1798,18 +1801,18 @@ class orm(orm_template): id_change = [] for tuple_fn in list_store: for id in ids: - id_change.append(self._store_get_ids(cr, uid, id,tuple_fn, context)[0]) - fn_data = id_change,tuple_fn + id_change.append(self._store_get_ids(cr, uid, id, tuple_fn, context)[0]) + fn_data = id_change, tuple_fn fn_list.append(fn_data) - delta= context.get('read_delta',False) + delta = context.get('read_delta', False) if delta and self._log_access: for i in range(0, len(ids), cr.IN_MAX): sub_ids = ids[i:i+cr.IN_MAX] cr.execute("select (now() - min(write_date)) <= '%s'::interval " \ "from \"%s\" where id in (%s)" % - (delta, self._table, ",".join(map(str, sub_ids))) ) - res= cr.fetchone() + (delta, self._table, ",".join(map(str, sub_ids)))) + res = cr.fetchone() if res and res[0]: raise except_orm(_('ConcurrencyException'), _('This record was modified in the meanwhile')) @@ -1832,7 +1835,7 @@ class orm(orm_template): for i in range(0, len(ids), cr.IN_MAX): sub_ids = ids[i:i+cr.IN_MAX] - str_d = string.join(('%d',)*len(sub_ids),',') + str_d = string.join(('%d',)*len(sub_ids), ',') if d1: cr.execute('SELECT id FROM "'+self._table+'" ' \ 'WHERE id IN ('+str_d+')'+d1, sub_ids+d2) @@ -1848,8 +1851,8 @@ class orm(orm_template): cr.execute('delete from "'+self._table+'" ' \ 'where id in ('+str_d+')', sub_ids) if fn_list: - for ids,tuple_fn in fn_list: - self._store_set_values(cr, uid, ids,tuple_fn,id_change, context) + for ids, tuple_fn in fn_list: + self._store_set_values(cr, uid, ids, tuple_fn, id_change, context) return True @@ -1888,19 +1891,19 @@ class orm(orm_template): vals.pop(field) if not context: - context={} + context = {} if not ids: return True if isinstance(ids, (int, long)): ids = [ids] - delta= context.get('read_delta',False) + delta = context.get('read_delta', False) if delta and self._log_access: for i in range(0, len(ids), cr.IN_MAX): sub_ids = ids[i:i+cr.IN_MAX] cr.execute("select (now() - min(write_date)) <= '%s'::interval " \ "from %s where id in (%s)" % - (delta,self._table, ",".join(map(str, sub_ids)))) - res= cr.fetchone() + (delta, self._table, ",".join(map(str, sub_ids)))) + res = cr.fetchone() if res and res[0]: for field in vals: if field in self._columns and self._columns[field]._classic_write: @@ -1911,10 +1914,10 @@ class orm(orm_template): #for v in self._inherits.values(): # assert v not in vals, (v, vals) - upd0=[] - upd1=[] - upd_todo=[] - updend=[] + upd0 = [] + upd1 = [] + upd_todo = [] + updend = [] direct = [] totranslate = context.get('lang', False) and (context['lang'] != 'en_US') for field in vals: @@ -1960,7 +1963,7 @@ class orm(orm_template): 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),',') + ids_str = string.join(map(str, sub_ids), ',') if d1: cr.execute('SELECT id FROM "'+self._table+'" ' \ 'WHERE id IN ('+ids_str+')'+d1, d2) @@ -1975,19 +1978,19 @@ class orm(orm_template): _('You try to write on an record that doesn\'t exist ' \ '(Document type: %s).') % self._description) if d1: - cr.execute('update "'+self._table+'" set '+string.join(upd0,',')+' ' \ + cr.execute('update "'+self._table+'" set '+string.join(upd0, ',')+' ' \ 'where id in ('+ids_str+')'+d1, upd1+ d2) else: - cr.execute('update "'+self._table+'" set '+string.join(upd0,',')+' ' \ + cr.execute('update "'+self._table+'" set '+string.join(upd0, ',')+' ' \ 'where id in ('+ids_str+')', upd1) if totranslate: for f in direct: if self._columns[f].translate: - self.pool.get('ir.translation')._set_ids(cr, user, self._name+','+f,'model', context['lang'], ids,vals[f]) + self.pool.get('ir.translation')._set_ids(cr, user, self._name+','+f, 'model', context['lang'], ids, vals[f]) # call the 'set' method of fields which are not classic_write - upd_todo.sort(lambda x,y: self._columns[x].priority-self._columns[y].priority) + upd_todo.sort(lambda x, y: self._columns[x].priority-self._columns[y].priority) for field in upd_todo: for id in ids: self._columns[field].set(cr, self, id, field, vals[field], user, context=context) @@ -1997,20 +2000,20 @@ class orm(orm_template): 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),',') + ids_str = string.join(map(str, sub_ids), ',') cr.execute('select distinct "'+col+'" from "'+self._table+'" ' \ 'where id in ('+ids_str+')', upd1) nids.extend([x[0] for x in cr.fetchall()]) v = {} for val in updend: - if self._inherit_fields[val][0]==table: - v[val]=vals[val] + if self._inherit_fields[val][0] == table: + v[val] = vals[val] self.pool.get(table).write(cr, user, nids, v, context) self._validate(cr, user, ids, context) - if context.has_key('read_delta'): + if 'read_delta' in context: del context['read_delta'] wf_service = netsvc.LocalService("workflow") @@ -2029,8 +2032,8 @@ class orm(orm_template): flag = True break if flag: - id_change = self._store_get_ids(cr, user, ids[0],tuple_fn, context) - self._store_set_values(cr, user, ids[0],tuple_fn,id_change, context) + id_change = self._store_get_ids(cr, user, ids[0], tuple_fn, context) + self._store_set_values(cr, user, ids[0], tuple_fn, id_change, context) return True @@ -2044,13 +2047,13 @@ class orm(orm_template): vals = dictionary of the form {'field_name':field_value, ...} """ if not context: - context={} + context = {} self.pool.get('ir.model.access').check(cr, user, self._name, 'create') default = [] avoid_table = [] - for (t,c) in self._inherits.items(): + for (t, c) in self._inherits.items(): if c in vals: avoid_table.append(t) for f in self._columns.keys(): # + self._inherit_fields.keys(): @@ -2073,7 +2076,7 @@ class orm(orm_template): for v in vals.keys(): if v in self._inherit_fields: - (table,col,col_detail) = self._inherit_fields[v] + (table, col, col_detail) = self._inherit_fields[v] tocreate[table][v] = vals[v] del vals[v] @@ -2087,8 +2090,8 @@ class orm(orm_template): for field in vals: if self._columns[field]._classic_write: - upd0=upd0+',"'+field+'"' - upd1=upd1+','+self._columns[field]._symbol_set[0] + upd0 = upd0 + ',"' + field + '"' + upd1 = upd1 + ',' + self._columns[field]._symbol_set[0] upd2.append(self._columns[field]._symbol_set[1](vals[field])) else: upd_todo.append(field) @@ -2115,7 +2118,7 @@ class orm(orm_template): upd1 += ',%d,now()' upd2.append(user) cr.execute('insert into "'+self._table+'" (id'+upd0+") values ("+str(id_new)+upd1+')', tuple(upd2)) - upd_todo.sort(lambda x,y: self._columns[x].priority-self._columns[y].priority) + upd_todo.sort(lambda x, y: self._columns[x].priority-self._columns[y].priority) for field in upd_todo: self._columns[field].set(cr, self, id_new, field, vals[field], user, context) @@ -2127,24 +2130,24 @@ class orm(orm_template): if self._name in self.pool._store_function.keys(): list_store = self.pool._store_function[self._name] for tuple_fn in list_store: - id_change = self._store_get_ids(cr, user, id_new,tuple_fn, context) - self._store_set_values(cr, user, id_new,tuple_fn,id_change, context) + id_change = self._store_get_ids(cr, user, id_new, tuple_fn, context) + self._store_set_values(cr, user, id_new, tuple_fn, id_change, context) return id_new - def _store_get_ids(self,cr, uid, ids,tuple_fn, context): - parent_id=getattr(self.pool.get(tuple_fn[0]),tuple_fn[4].func_name)(cr,uid,[ids]) + def _store_get_ids(self, cr, uid, ids, tuple_fn, context): + parent_id = getattr(self.pool.get(tuple_fn[0]), tuple_fn[4].func_name)(cr, uid, [ids]) return parent_id - def _store_set_values(self,cr,uid,ids,tuple_fn,parent_id,context): + def _store_set_values(self, cr, uid, ids, tuple_fn, parent_id, context): name = tuple_fn[1] table = tuple_fn[0] - args={} - vals_tot=getattr(self.pool.get(table),tuple_fn[2])(cr, uid, parent_id ,name, args, context) + args = {} + vals_tot = getattr(self.pool.get(table), tuple_fn[2])(cr, uid, parent_id, name, args, context) write_dict = {} for id in vals_tot.keys(): - write_dict[name]=vals_tot[id] - self.pool.get(table).write(cr,uid,[id],write_dict) + write_dict[name] = vals_tot[id] + self.pool.get(table).write(cr, uid, [id], write_dict) return True def _update_function_stored(self, cr, user, ids, context=None): @@ -2153,10 +2156,10 @@ class orm(orm_template): f = filter(lambda a: isinstance(self._columns[a], fields.function) \ and self._columns[a].store, self._columns) if f: - result=self.read(cr, user, ids, fields=f, context=context) + result = self.read(cr, user, ids, fields=f, context=context) for res in result: - upd0=[] - upd1=[] + upd0 = [] + upd1 = [] for field in res: if field not in f: continue @@ -2179,7 +2182,7 @@ class orm(orm_template): # TODO: ameliorer avec NULL def _where_calc(self, cr, user, args, active_test=True, context=None): if not context: - context={} + context = {} args = args[:] # if the object has a field named 'active', filter out all inactive # records unless they were explicitely asked for @@ -2196,7 +2199,7 @@ class orm(orm_template): else: qu1, qu2, tables = [], [], ['"%s"' % self._table] - return (qu1,qu2,tables) + return (qu1, qu2, tables) def _check_qorder(self, word): if not regex_order.match(word): @@ -2208,10 +2211,10 @@ class orm(orm_template): if not context: context = {} # compute the where, order by, limit and offset clauses - (qu1,qu2,tables) = self._where_calc(cr, user, args, context=context) + (qu1, qu2, tables) = self._where_calc(cr, user, args, context=context) if len(qu1): - qu1 = ' where '+string.join(qu1,' and ') + qu1 = ' where '+string.join(qu1, ' and ') else: qu1 = '' @@ -2244,15 +2247,15 @@ class orm(orm_template): # a char field def distinct_field_get(self, cr, uid, field, value, args=None, offset=0, limit=None): if not args: - args=[] + args = [] if field in self._inherit_fields: - return self.pool.get(self._inherit_fields[field][0]).distinct_field_get(cr, uid,field,value,args,offset,limit) + return self.pool.get(self._inherit_fields[field][0]).distinct_field_get(cr, uid, field, value, args, offset, limit) else: return self._columns[field].search(cr, self, args, field, value, offset, limit, uid) def name_get(self, cr, user, ids, context=None): if not context: - context={} + context = {} if not ids: return [] if isinstance(ids, (int, long)): @@ -2262,19 +2265,19 @@ class orm(orm_template): def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=80): if not args: - args=[] + args = [] if not context: - context={} - args=args[:] + context = {} + args = args[:] if name: - args += [(self._rec_name,operator,name)] + args += [(self._rec_name, operator, name)] ids = self.search(cr, user, args, limit=limit, context=context) res = self.name_get(cr, user, ids, context) return res def copy(self, cr, uid, id, default=None, context=None): if not context: - context={} + context = {} if not default: default = {} if 'state' not in default: @@ -2315,7 +2318,7 @@ class orm(orm_template): def read_string(self, cr, uid, id, langs, fields=None, context=None): if not context: - context={} + context = {} res = {} res2 = {} self.pool.get('ir.model.access').check(cr, uid, 'ir.translation', 'read') @@ -2327,22 +2330,22 @@ class orm(orm_template): if f in self._columns: res_trans = self.pool.get('ir.translation')._get_source(cr, uid, self._name+','+f, 'field', lang) if res_trans: - res[lang][f]=res_trans + res[lang][f] = res_trans else: - res[lang][f]=self._columns[f].string + res[lang][f] = self._columns[f].string for table in self._inherits: cols = intersect(self._inherit_fields.keys(), fields) res2 = self.pool.get(table).read_string(cr, uid, id, langs, cols, context) for lang in res2: if lang in res: - res[lang]={'code': lang} + res[lang] = {'code': lang} for f in res2[lang]: - res[lang][f]=res2[lang][f] + res[lang][f] = res2[lang][f] return res def write_string(self, cr, uid, id, langs, vals, context=None): if not context: - context={} + context = {} self.pool.get('ir.model.access').check(cr, uid, 'ir.translation', 'write') for lang in langs: for field in vals: diff --git a/bin/osv/osv.py b/bin/osv/osv.py index 369f006c9df..7ce0c0e85b5 100644 --- a/bin/osv/osv.py +++ b/bin/osv/osv.py @@ -28,334 +28,2351 @@ ############################################################################### # -# OSV: Objects Services +# Object relationnal mapping to postgresql module +# . Hierarchical structure +# . Constraints consistency, validations +# . Object meta Data depends on its status +# . Optimised processing by complex query (multiple actions at once) +# . Default fields value +# . Permissions optimisation +# . Persistant object: DB postgresql +# . Datas conversions +# . Multi-level caching system +# . 2 different inheritancies +# . Fields: +# - classicals (varchar, integer, boolean, ...) +# - relations (one2many, many2one, many2many) +# - functions +# # -import orm +import time +import types +from xml import dom, xpath import netsvc -import pooler -import copy -import sys +import re -import psycopg -from netsvc import Logger, LOG_ERROR -from tools.misc import UpdateableDict +import fields +import tools -module_list = [] -module_class_list = {} -class_pool = {} +from tools.config import config -class except_osv(Exception): - def __init__(self, name, value, exc_type='warning'): +regex_order = re.compile('^([a-zA-Z0-9_]+( desc)?( asc)?,?)+$', re.I) + + +def intersect(la, lb): + return filter(lambda x: x in lb, la) + + +class except_orm(Exception): + def __init__(self, name, value): self.name = name - self.exc_type = exc_type self.value = value - self.args = (exc_type,name) + self.args = (name, value) -class osv_pool(netsvc.Service): + +# Readonly python database object browser +class browse_null(object): def __init__(self): - self.obj_pool = {} - self.module_object_list = {} - self.created = [] - self._sql_error = {} - self._store_function = {} - netsvc.Service.__init__(self, 'object_proxy', audience='') - self.joinGroup('web-services') - self.exportMethod(self.exportedMethods) - self.exportMethod(self.obj_list) - self.exportMethod(self.exec_workflow) - self.exportMethod(self.execute) - self.exportMethod(self.execute_cr) + self.id = False - def execute_cr(self, cr, uid, obj, method, *args, **kw): - try: - object = pooler.get_pool(cr.dbname).get(obj) - if not object: - self.abortResponse(1, 'Object Error', 'warning', - 'Object %s doesn\'t exist' % str(obj)) - return getattr(object,method)(cr, uid, *args, **kw) - except orm.except_orm, inst: - self.abortResponse(1, inst.name, 'warning', inst.value) - except except_osv, inst: - self.abortResponse(1, inst.name, inst.exc_type, inst.value) - except psycopg.IntegrityError, inst: - for key in self._sql_error.keys(): - if key in inst[0]: - self.abortResponse(1, 'Constraint Error', 'warning', - self._sql_error[key]) - self.abortResponse(1, 'Integrity Error', 'warning', inst[0]) - except Exception, e: - import traceback - tb_s = reduce(lambda x, y: x+y, traceback.format_exception( - sys.exc_type, sys.exc_value, sys.exc_traceback)) - logger = Logger() - logger.notifyChannel("web-services", LOG_ERROR, - 'Exception in call: ' + tb_s) - raise + def __getitem__(self, name): + return False - def execute(self, db, uid, obj, method, *args, **kw): - db, pool = pooler.get_db_and_pool(db) - cr = db.cursor() - try: - try: - res = pool.execute_cr(cr, uid, obj, method, *args, **kw) - cr.commit() - except Exception: - cr.rollback() - raise - finally: - cr.close() - return res + def __int__(self): + return False - def exec_workflow_cr(self, cr, uid, obj, method, *args): - wf_service = netsvc.LocalService("workflow") - return wf_service.trg_validate(uid, obj, args[0], method, cr) + def __str__(self): + return '' - def exec_workflow(self, db, uid, obj, method, *args): - cr = pooler.get_db(db).cursor() - try: - try: - res = self.exec_workflow_cr(cr, uid, obj, method, *args) - cr.commit() - except orm.except_orm, inst: - cr.rollback() - self.abortResponse(1, inst.name, 'warning', inst.value) - except except_osv, inst: - cr.rollback() - self.abortResponse(1, inst.name, inst[0], inst.value) - finally: - cr.close() - return res - - def obj_list(self): - return self.obj_pool.keys() - - # adds a new object instance to the object pool. - # if it already existed, the instance is replaced - def add(self, name, obj_inst): - if self.obj_pool.has_key(name): - del self.obj_pool[name] - self.obj_pool[name] = obj_inst - - module = str(obj_inst.__class__)[6:] - module = module[:len(module)-1] - module = module.split('.')[0][2:] - self.module_object_list.setdefault(module, []).append(obj_inst) - - def get(self, name): - obj = self.obj_pool.get(name, None) -# We cannot uncomment this line because it breaks initialisation since objects do not initialize -# in the correct order and the ORM doesnt support correctly when some objets do not exist yet -# assert obj, "object %s does not exist !" % name - return obj - - #TODO: pass a list of modules to load - def instanciate(self, module, cr): -# print "module list:", module_list -# for module in module_list: - res = [] - class_list = module_class_list.get(module, []) -# if module not in self.module_object_list: -# print "%s class_list:" % module, class_list - for klass in class_list: - res.append(klass.createInstance(self, module, cr)) - return res -# else: -# print "skipping module", module - -#pooler.get_pool(cr.dbname) = osv_pool() - -class osv_memory(orm.orm_memory): - #__metaclass__ = inheritor - def __new__(cls): - module = str(cls)[6:] - module = module[:len(module)-1] - module = module.split('.')[0][2:] - if not hasattr(cls, '_module'): - cls._module = module - module_class_list.setdefault(cls._module, []).append(cls) - class_pool[cls._name] = cls - if module not in module_list: - module_list.append(cls._module) - return None - - # - # Goal: try to apply inheritancy at the instanciation level and - # put objects in the pool var - # - def createInstance(cls, pool, module, cr): - name = hasattr(cls,'_name') and cls._name or cls._inherit - parent_name = hasattr(cls, '_inherit') and cls._inherit - if parent_name: - print 'Inherit not supported in osv_memory object !' - obj = object.__new__(cls) - obj.__init__(pool, cr) - return obj - createInstance = classmethod(createInstance) - - def __init__(self, pool, cr): - pool.add(self._name, self) - self.pool = pool - orm.orm_memory.__init__(self, cr) + def __nonzero__(self): + return False +# +# TODO: execute an object method on browse_record_list +# +class browse_record_list(list): -class osv(orm.orm): - #__metaclass__ = inheritor - def __new__(cls): - module = str(cls)[6:] - module = module[:len(module)-1] - module = module.split('.')[0][2:] - if not hasattr(cls, '_module'): - cls._module = module - module_class_list.setdefault(cls._module, []).append(cls) - class_pool[cls._name] = cls - if module not in module_list: - module_list.append(cls._module) - return None + def __init__(self, lst, context=None): + if not context: + context = {} + super(browse_record_list, self).__init__(lst) + self.context = context - # - # Goal: try to apply inheritancy at the instanciation level and - # put objects in the pool var - # - def createInstance(cls, pool, module, cr): - parent_name = hasattr(cls, '_inherit') and cls._inherit - if parent_name: - parent_class = pool.get(parent_name).__class__ - assert parent_class, "parent class %s does not exist !" % parent_name - nattr = {} - for s in ('_columns', '_defaults', '_inherits', '_constraints', '_sql_constraints'): - new = copy.copy(getattr(pool.get(parent_name), s)) - if hasattr(new, 'update'): - new.update(cls.__dict__.get(s, {})) + +class browse_record(object): + def __init__(self, cr, uid, id, table, cache, context=None, list_class = None, fields_process={}): + ''' + table : the object (inherited from orm) + context : a dictionnary with an optionnal context + ''' + if not context: + context = {} + assert id, _('Wrong ID for the browse record, got %s, expected an integer.') % str(id) + self._list_class = list_class or browse_record_list + self._cr = cr + self._uid = uid + self._id = id + self._table = table + self._table_name = self._table._name + self._context = context + self._fields_process = fields_process + + cache.setdefault(table._name, {}) + self._data = cache[table._name] + if not id in self._data: + self._data[id] = {'id': id} + self._cache = cache + + def __getitem__(self, name): + if name == 'id': + return self._id + if not name in self._data[self._id]: + # build the list of fields we will fetch + + # fetch the definition of the field which was asked for + if name in self._table._columns: + col = self._table._columns[name] + elif name in self._table._inherit_fields: + col = self._table._inherit_fields[name][2] + elif hasattr(self._table, name): + if isinstance(getattr(self._table, name), (types.MethodType, types.LambdaType, types.FunctionType)): + return lambda *args, **argv: getattr(self._table, name)(self._cr, self._uid, [self._id], *args, **argv) else: - new.extend(cls.__dict__.get(s, [])) - nattr[s] = new - name = hasattr(cls,'_name') and cls._name or cls._inherit - cls = type(name, (cls, parent_class), nattr) - obj = object.__new__(cls) - obj.__init__(pool, cr) - return obj - createInstance = classmethod(createInstance) + return getattr(self._table, name) + else: + logger = netsvc.Logger() + logger.notifyChannel('orm', netsvc.LOG_ERROR, "Programming error: field '%s' does not exist in object '%s' !" % (name, self._table._name)) + return False - def __init__(self, pool, cr): - pool.add(self._name, self) - self.pool = pool - orm.orm.__init__(self, cr) + # if the field is a classic one or a many2one, we'll fetch all classic and many2one fields + if col._classic_write: + # gen the list of "local" (ie not inherited) fields which are classic or many2one + ffields = filter(lambda x: x[1]._classic_write, self._table._columns.items()) + # gen the list of inherited fields + inherits = map(lambda x: (x[0], x[1][2]), self._table._inherit_fields.items()) + # complete the field list with the inherited fields which are classic or many2one + ffields += filter(lambda x: x[1]._classic_write, inherits) + # otherwise we fetch only that field + else: + ffields = [(name, col)] + ids = filter(lambda id: not name in self._data[id], self._data.keys()) + # read the data + fffields = map(lambda x: x[0], ffields) + datas = self._table.read(self._cr, self._uid, ids, fffields, context=self._context, load="_classic_write") + if self._fields_process: + for n, f in ffields: + if f._type in self._fields_process: + for d in datas: + d[n] = self._fields_process[f._type](d[n]) + d[n].set_value(d[n], self, f) -class Cacheable(object): - _cache = UpdateableDict() + # create browse records for 'remote' objects + for data in datas: + for n, f in ffields: + if f._type in ('many2one', 'one2one'): + if data[n]: + obj = self._table.pool.get(f._obj) + compids = False + if not f._classic_write: + ids2 = data[n][0] + 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) + else: + data[n] = browse_null() + else: + 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) + return self._data[self._id][name] - def add(self, key, value): - self._cache[key] = value + def __getattr__(self, name): +# raise an AttributeError exception. + return self[name] - def invalidate(self, key): - del self._cache[key] + def __contains__(self, name): + return (name in self._table._columns) or (name in self._table._inherit_fields) or hasattr(self._table, name) - def get(self, key): - try: - w = self._cache[key] - return w - except KeyError: - return None + def __hasattr__(self, name): + return name in self + + def __int__(self): + return self._id + + def __str__(self): + return "browse_record(%s, %d)" % (self._table_name, self._id) + + def __eq__(self, other): + return (self._table_name, self._id) == (other._table_name, other._id) + + def __ne__(self, other): + return (self._table_name, self._id) != (other._table_name, other._id) + + # we need to define __unicode__ even though we've already defined __str__ + # because we have overridden __getattr__ + def __unicode__(self): + return unicode(str(self)) + + def __hash__(self): + return hash((self._table_name, self._id)) + + __repr__ = __str__ + + +def get_pg_type(f): + ''' + returns a tuple + (type returned by postgres when the column was created, type expression to create the column) + ''' + + type_dict = { + fields.boolean: 'bool', + fields.integer: 'int4', + fields.text: 'text', + fields.date: 'date', + fields.time: 'time', + fields.datetime: 'timestamp', + fields.binary: 'bytea', + fields.many2one: 'int4', + } + if type(f) in type_dict: + 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])) + else: + f_type = ('float8', 'DOUBLE PRECISION') + elif isinstance(f, (fields.char, fields.reference)): + f_type = ('varchar', 'VARCHAR(%d)' % (f.size,)) + elif isinstance(f, fields.selection): + if isinstance(f.selection, list) and isinstance(f.selection[0][0], (str, unicode)): + f_size = reduce(lambda x, y: max(x, len(y[0])), f.selection, f.size or 16) + elif isinstance(f.selection, list) and isinstance(f.selection[0][0], int): + f_size = -1 + else: + f_size = (hasattr(f, 'size') and f.size) or 16 + + if f_size == -1: + f_type = ('int4', 'INTEGER') + else: + f_type = ('varchar', 'VARCHAR(%d)' % f_size) + elif isinstance(f, fields.function) and eval('fields.'+(f._type)) in type_dict: + t = eval('fields.'+(f._type)) + f_type = (type_dict[t], type_dict[t]) + elif isinstance(f, fields.function) and f._type == 'float': + f_type = ('float8', 'DOUBLE PRECISION') + elif isinstance(f, fields.function) and f._type == 'selection': + f_type = ('text', 'text') + else: + logger = netsvc.Logger() + logger.notifyChannel("init", netsvc.LOG_WARNING, '%s type not supported!' % (type(f))) + f_type = None + return f_type + + +class orm_template(object): + _name = None + _columns = {} + _constraints = [] + _defaults = {} + _rec_name = 'name' + _parent_name = 'parent_id' + _date_name = 'date' + _order = 'id' + _sequence = None + _description = None + _inherits = {} + _table = None + + def _field_create(self, cr, context={}): + cr.execute("SELECT id FROM ir_model WHERE model='%s'" % self._name) + if not cr.rowcount: + # reference model in order to have a description of its fonctionnality in custom_report + cr.execute('SELECT nextval(%s)', ('ir_model_id_seq',)) + id = cr.fetchone()[0] + cr.execute("INSERT INTO ir_model (id,model, name, info) VALUES (%s, %s, %s, %s)", (id, self._name, self._description, self.__doc__)) + if 'module' in context: + cr.execute("INSERT INTO ir_model_data (name,date_init,date_update,module,model,res_id) VALUES (%s, now(), now(), %s, %s, %s)", \ + ('model_'+self._table, context['module'], 'ir.model', id) + ) + cr.commit() + + cr.execute("SELECT * FROM ir_model_fields WHERE model=%s", (self._name,)) + cols = {} + for rec in cr.dictfetchall(): + cols[rec['name']] = rec + + cr.execute("SELECT id FROM ir_model WHERE model='%s'" % self._name) + model_id = cr.fetchone()[0] + + for (k, f) in self._columns.items(): + vals = { + 'model_id': model_id, + 'model': self._name, + 'name': k, + 'field_description': f.string.replace("'", " "), + 'ttype': f._type, + 'relate': (f.relate and 1) or 0, + 'relation': f._obj or 'NULL', + 'group_name': f.group_name or '', + 'view_load': (f.view_load and 1) or 0, + 'select_level': str(f.select or 0) + } + if k not in cols: + cr.execute('select nextval(%s)', ('ir_model_fields_id_seq',)) + id = cr.fetchone()[0] + vals['id'] = id + cr.execute("""INSERT INTO ir_model_fields ( + id, model_id, model, name, field_description, ttype, + relate,relation,group_name,view_load,state,select_level + ) VALUES ( + %d,%s,%s,%s,%s,%s, %s,%s,%s,%s,%s, %s + )""", ( + id, vals['model_id'], vals['model'], vals['name'], vals['field_description'], vals['ttype'], + bool(vals['relate']), vals['relation'], vals['group_name'], bool(vals['view_load']), 'base', + vals['select_level'] + )) + if 'module' in context: + cr.execute("INSERT INTO ir_model_data (name,date_init,date_update,module,model,res_id) VALUES (%s, now(), now(), %s, %s, %s)", \ + (('field_'+self._table+'_'+k)[:64], context['module'], 'ir.model.fields', id) + ) + else: + for key, val in vals.items(): + if cols[k][key] != vals[key]: + print 'Different', cols[k][key], vals[key], k, key, self._name + cr.execute('update ir_model_fields set field_description=%s where model=%s and name=%s', (vals['field_description'], vals['model'], vals['name'])) + cr.commit() + cr.execute("""UPDATE ir_model_fields SET + model_id=%s, field_description=%s, ttype=%s, relate=%s, relation=%s, + group_name=%s, view_load=%s, select_level=%s + WHERE + model=%s AND name=%s""", ( + vals['model_id'], vals['field_description'], vals['ttype'], bool(vals['relate']), + vals['relation'], vals['group_name'], bool(vals['view_load']), + vals['select_level'], vals['model'], vals['name'] + )) + continue + cr.commit() + + def _auto_init(self, cr, context={}): + self._field_create(cr, context) + + def __init__(self, cr): + if not self._description: + self._description = self._name + if not self._table: + self._table = self._name.replace('.', '_') + + def browse(self, cr, uid, select, context=None, list_class=None, fields_process={}): + if not context: + context = {} + self._list_class = list_class or browse_record_list + cache = {} + # need to accepts ints and longs because ids coming from a method + # launched by button in the interface have a type long... + if isinstance(select, (int, long)): + return browse_record(cr, uid, select, self, cache, context=context, list_class=self._list_class, fields_process=fields_process) + elif isinstance(select, list): + return self._list_class([browse_record(cr, uid, id, self, cache, context=context, list_class=self._list_class, fields_process=fields_process) for id in select], context) + else: + return browse_null() + + def __export_row(self, cr, uid, row, fields, context=None): + lines = [] + data = map(lambda x: '', range(len(fields))) + done = [] + for fpos in range(len(fields)): + f = fields[fpos] + if f: + r = row + i = 0 + while i < len(f): + r = r[f[i]] + if not r: + break + if isinstance(r, (browse_record_list, list)): + first = True + fields2 = map(lambda x: (x[:i+1]==f[:i+1] and x[i+1:]) \ + or [], fields) + if fields2 in done: + break + done.append(fields2) + for row2 in r: + lines2 = self.__export_row(cr, uid, row2, fields2, + context) + if first: + for fpos2 in range(len(fields)): + if lines2 and lines2[0][fpos2]: + data[fpos2] = lines2[0][fpos2] + lines += lines2[1:] + first = False + else: + lines += lines2 + break + i += 1 + if i == len(f): + data[fpos] = str(r or '') + return [data] + lines + + def export_data(self, cr, uid, ids, fields, context=None): + if not context: + context = {} + fields = map(lambda x: x.split('/'), fields) + datas = [] + for row in self.browse(cr, uid, ids, context): + datas += self.__export_row(cr, uid, row, fields, context) + return datas + + def import_data(self, cr, uid, fields, datas, mode='init', + current_module=None, noupdate=False, context=None): + if not context: + context = {} + fields = map(lambda x: x.split('/'), fields) + logger = netsvc.Logger() + + def process_liness(self, datas, prefix, fields_def, position=0): + line = datas[position] + row = {} + translate = {} + todo = [] + warning = '' + data_id = False + # + # Import normal fields + # + for i in range(len(fields)): + if i >= len(line): + raise Exception(_('Please check that all your lines have %d columns.') % (len(fields),)) + field = fields[i] + if field == ["id"]: + data_id = line[i] + continue + if (len(field)==len(prefix)+1) and field[len(prefix)].endswith(':id'): + res_id = False + if line[i]: + if fields_def[field[len(prefix)][:-3]]['type']=='many2many': + res_id = [] + for word in line[i].split(','): + if '.' in word: + module, xml_id = word.rsplit('.', 1) + else: + module, xml_id = current_module, word + ir_model_data_obj = self.pool.get('ir.model.data') + id = ir_model_data_obj._get_id(cr, uid, module, + xml_id) + res_id2 = ir_model_data_obj.read(cr, uid, [id], + ['res_id'])[0]['res_id'] + if res_id2: + res_id.append(res_id2) + if len(res_id): + res_id = [(6, 0, res_id)] + else: + if '.' in line[i]: + module, xml_id = line[i].rsplit('.', 1) + else: + module, xml_id = current_module, line[i] + ir_model_data_obj = self.pool.get('ir.model.data') + id = ir_model_data_obj._get_id(cr, uid, module, xml_id) + res_id = ir_model_data_obj.read(cr, uid, [id], + ['res_id'])[0]['res_id'] + row[field[0][:-3]] = res_id or False + continue + if (len(field) == len(prefix)+1) and \ + len(field[len(prefix)].split(':lang=')) == 2: + f, lang = field[len(prefix)].split(':lang=') + translate.setdefault(lang, {})[f]=line[i] or False + continue + if (len(field) == len(prefix)+1) and \ + (prefix == field[0:len(prefix)]): + if fields_def[field[len(prefix)]]['type'] == 'integer': + res = line[i] and int(line[i]) + elif fields_def[field[len(prefix)]]['type'] == 'float': + res = line[i] and float(line[i]) + elif fields_def[field[len(prefix)]]['type'] == 'selection': + res = False + if isinstance(fields_def[field[len(prefix)]]['selection'], + (tuple, list)): + sel = fields_def[field[len(prefix)]]['selection'] + else: + sel = fields_def[field[len(prefix)]]['selection'](self, + cr, uid, context) + for key, val in sel: + if str(key) == line[i]: + res = key + if line[i] and not res: + logger.notifyChannel("import", netsvc.LOG_WARNING, + "key '%s' not found in selection field '%s'" % \ + (line[i], field[len(prefix)])) + elif fields_def[field[len(prefix)]]['type']=='many2one': + res = False + if line[i]: + relation = fields_def[field[len(prefix)]]['relation'] + res2 = self.pool.get(relation).name_search(cr, uid, + line[i], [], operator='=') + res = (res2 and res2[0][0]) or False + if not res: + warning += ('Relation not found: ' + line[i] + \ + ' on ' + relation + ' !\n') + logger.notifyChannel("import", netsvc.LOG_WARNING, + 'Relation not found: ' + line[i] + \ + ' on ' + relation + ' !\n') + elif fields_def[field[len(prefix)]]['type']=='many2many': + res = [] + if line[i]: + relation = fields_def[field[len(prefix)]]['relation'] + for word in line[i].split(','): + res2 = self.pool.get(relation).name_search(cr, + uid, word, [], operator='=') + res3 = (res2 and res2[0][0]) or False + if not res3: + warning += ('Relation not found: ' + \ + line[i] + ' on '+relation + ' !\n') + logger.notifyChannel("import", + netsvc.LOG_WARNING, + 'Relation not found: ' + line[i] + \ + ' on '+relation + ' !\n') + else: + res.append(res3) + if len(res): + res = [(6, 0, res)] + else: + res = line[i] or False + row[field[len(prefix)]] = res + elif (prefix==field[0:len(prefix)]): + if field[0] not in todo: + todo.append(field[len(prefix)]) + # + # Import one2many fields + # + nbrmax = 1 + for field in todo: + newfd = self.pool.get(fields_def[field]['relation']).fields_get( + cr, uid, context=context) + res = process_liness(self, datas, prefix + [field], newfd, position) + (newrow, max2, w2, translate2, data_id2) = res + nbrmax = max(nbrmax, max2) + warning = warning + w2 + reduce(lambda x, y: x and y, newrow) + row[field] = (reduce(lambda x, y: x or y, newrow.values()) and \ + [(0, 0, newrow)]) or [] + i = max2 + while (position+i)\n' + '\n' + ' \n' + '') % (self._rec_name) + + return arch + + # + # if view_id, view_type is not required + # + def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False): + if not context: + context = {} + def _inherit_apply(src, inherit): + def _find(node, node2): + # Check if xpath query or normal inherit (with field matching) + if node2.nodeType == node2.ELEMENT_NODE and node2.localName == 'xpath': + res = xpath.Evaluate(node2.getAttribute('expr'), node) + return res and res[0] + else: + if node.nodeType == node.ELEMENT_NODE and node.localName == node2.localName: + res = True + for attr in node2.attributes.keys(): + if attr == 'position': + continue + if node.hasAttribute(attr): + if node.getAttribute(attr)==node2.getAttribute(attr): + continue + res = False + if res: + return node + for child in node.childNodes: + res = _find(child, node2) + if res: + return res + return None + + doc_src = dom.minidom.parseString(src) + doc_dest = dom.minidom.parseString(inherit) + toparse = doc_dest.childNodes + while len(toparse): + node2 = toparse.pop(0) + if not node2.nodeType == node2.ELEMENT_NODE: + continue + if node2.localName == 'data': + toparse += node2.childNodes + continue + node = _find(doc_src, node2) + if node: + pos = 'inside' + if node2.hasAttribute('position'): + pos = node2.getAttribute('position') + if pos == 'replace': + parent = node.parentNode + for child in node2.childNodes: + if child.nodeType == child.ELEMENT_NODE: + parent.insertBefore(child, node) + parent.removeChild(node) + else: + for child in node2.childNodes: + if child.nodeType == child.ELEMENT_NODE: + if pos == 'inside': + node.appendChild(child) + elif pos == 'after': + sib = node.nextSibling + if sib: + node.parentNode.insertBefore(child, sib) + else: + node.parentNode.appendChild(child) + elif pos=='before': + node.parentNode.insertBefore(child, node) + else: + raise AttributeError(_('Unknown position in inherited view %s !') % pos) + else: + attrs = ''.join([ + ' %s="%s"' % (attr, node2.getAttribute(attr)) + for attr in node2.attributes.keys() + if attr != 'position' + ]) + tag = "<%s%s>" % (node2.localName, attrs) + raise AttributeError(_("Couldn't find tag '%s' in parent view !") % tag) + return doc_src.toxml(encoding="utf-8").replace('\t', '') + + result = {'type': view_type, 'model': self._name} + + ok = True + model = True + sql_res = False + while ok: + if view_id: + where = (model and (" and model='%s'" % (self._name,))) or '' + cr.execute('SELECT arch,name,field_parent,id,type,inherit_id FROM ir_ui_view WHERE id=%d'+where, (view_id,)) + else: + cr.execute('SELECT arch,name,field_parent,id,type,inherit_id FROM ir_ui_view WHERE model=%s AND type=%s ORDER BY priority', (self._name, view_type)) + sql_res = cr.fetchone() + if not sql_res: + break + ok = sql_res[5] + view_id = ok or sql_res[3] + model = False + + # if a view was found + if sql_res: + result['type'] = sql_res[4] + result['view_id'] = sql_res[3] + result['arch'] = sql_res[0] + + def _inherit_apply_rec(result, inherit_id): + # get all views which inherit from (ie modify) this view + cr.execute('select arch,id from ir_ui_view where inherit_id=%d and model=%s order by priority', (inherit_id, self._name)) + sql_inherit = cr.fetchall() + for (inherit, id) in sql_inherit: + result = _inherit_apply(result, inherit) + result = _inherit_apply_rec(result, id) + return result + + result['arch'] = _inherit_apply_rec(result['arch'], sql_res[3]) + + result['name'] = sql_res[1] + result['field_parent'] = sql_res[2] or False + else: + # otherwise, build some kind of default view + if view_type == 'form': + res = self.fields_get(cr, user, context=context) + xml = '''''' \ + '''
''' % (self._description,) + for x in res: + if res[x]['type'] not in ('one2many', 'many2many'): + xml += '' % (x,) + if res[x]['type'] == 'text': + xml += "" + xml += "" + elif view_type == 'tree': + xml = '''''' \ + '''''' \ + % (self._description, self._rec_name) + elif view_type == 'calendar': + xml = self.__get_default_calendar_view() + else: + xml = '' + result['arch'] = xml + result['name'] = 'default' + result['field_parent'] = False + result['view_id'] = 0 + + doc = dom.minidom.parseString(result['arch'].encode('utf-8')) + xarch, xfields = self.__view_look_dom_arch(cr, user, doc, context=context) + result['arch'] = xarch + result['fields'] = xfields + if toolbar: + def clean(x): + x = x[2] + for key in ('report_sxw_content', 'report_rml_content', + 'report_sxw', 'report_rml', + 'report_sxw_content_data', 'report_rml_content_data'): + if key in x: + del x[key] + return x + ir_values_obj = self.pool.get('ir.values') + resprint = ir_values_obj.get(cr, user, 'action', + 'client_print_multi', [(self._name, False)], False, + context) + resaction = ir_values_obj.get(cr, user, 'action', + 'client_action_multi', [(self._name, False)], False, + context) + + resrelate = ir_values_obj.get(cr, user, 'action', + 'client_action_relate', [(self._name, False)], False, + context) + resprint = map(clean, 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) + resrelate = map(lambda x: x[2], resrelate) + + for x in resprint+resaction+resrelate: + x['string'] = x['name'] + + result['toolbar'] = { + 'print': resprint, + 'action': resaction, + 'relate': resrelate + } + return result + + _view_look_dom_arch = __view_look_dom_arch + + def search_count(self, cr, user, args, context=None): + if not context: + context = {} + res = self.search(cr, user, args, context=context, count=True) + if isinstance(res, list): + return len(res) + return res + + def search(self, cr, user, args, offset=0, limit=None, order=None, + context=None, count=False): + raise _('The search method is not implemented on this object !') + + def name_get(self, cr, user, ids, context=None): + raise _('The name_get method is not implemented on this object !') + + def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=80): + raise _('The name_search method is not implemented on this object !') + + def copy(self, cr, uid, id, default=None, context=None): + raise _('The copy method is not implemented on this object !') + + +class orm_memory(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'] + _inherit_fields = {} + _max_count = 200 + _max_hours = 1 + _check_time = 20 + + def __init__(self, cr): + super(orm_memory, self).__init__(cr) + self.datas = {} + self.next_id = 0 + self.check_id = 0 + cr.execute('delete from wkf_instance where res_type=%s', (self._name,)) def clear(self): - self._cache.clear() - self._items = [] - -def filter_dict(d, fields): - res = {} - for f in fields + ['id']: - if f in d: - res[f] = d[f] - return res - -class cacheable_osv(osv, Cacheable): - - _relevant = ['lang'] - - def __init__(self): - super(cacheable_osv, self).__init__() + self.check_id += 1 + if self.check_id % self._check_time: + return True + tounlink = [] + max = time.time() - self._max_hours * 60 * 60 + for id in self.datas: + if self.datas[id]['internal.date_access'] < max: + tounlink.append(id) + self.unlink(tounlink) + if len(self.datas)>self._max_count: + sorted = map(lambda x: (x[1]['internal.date_access'], x[0]), self.datas.items()) + sorted.sort() + ids = map(lambda x: x[1], sorted[:len(self.datas)-self._max_count]) + self.unlink(ids) + return True def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'): if not fields: - fields=[] - if not context: - context={} - fields = fields or self._columns.keys() - ctx = [context.get(x, False) for x in self._relevant] - result, tofetch = [], [] + fields = self._columns.keys() + result = [] for id in ids: - res = self.get(self._name, id, ctx) - if not res: - tofetch.append(id) - else: - result.append(filter_dict(res, fields)) - - # gen the list of "local" (ie not inherited) fields which are classic or many2one - nfields = filter(lambda x: x[1]._classic_write, self._columns.items()) - # gen the list of inherited fields - inherits = map(lambda x: (x[0], x[1][2]), self._inherit_fields.items()) - # complete the field list with the inherited fields which are classic or many2one - nfields += filter(lambda x: x[1]._classic_write, inherits) - nfields = [x[0] for x in nfields] - - res = super(cacheable_osv, self).read(cr, user, tofetch, nfields, context, load) - for r in res: - self.add((self._name, r['id'], ctx), r) - result.append(filter_dict(r, fields)) - - # Appel de fonction si necessaire - tofetch = [] - for f in fields: - if f not in nfields: - tofetch.append(f) - for f in tofetch: - fvals = self._columns[f].get(cr, self, ids, f, user, context=context) - for r in result: - r[f] = fvals[r['id']] - - # TODO: tri par self._order !! + r = {'id': id} + for f in fields: + r[f] = self.datas[id].get(f, False) + result.append(r) + self.datas[id]['internal.date_access'] = time.time() + fields_post = filter(lambda x: x in self._columns and not getattr(self._columns[x], load), fields) + for f in fields_post: + res2 = self._columns[f].get_memory(cr, self, ids, f, user, context=context, values=False) + for record in result: + record[f] = res2[record['id']] return result - def invalidate(self, key): - del self._cache[key[0]][key[1]] - - def write(self, cr, user, ids, values, context=None): - if not context: - context={} - for id in ids: - self.invalidate((self._name, id)) - return super(cacheable_osv, self).write(cr, user, ids, values, context) - - def unlink(self, cr, user, ids): + def write(self, cr, user, ids, vals, context=None): + vals2 = {} + upd_todo = [] + for field in vals: + if self._columns[field]._classic_write: + vals2[field] = vals[field] + else: + upd_todo.append(field) + for id_new in ids: + self.datas[id_new].update(vals2) + self.datas[id_new]['internal.date_access'] = time.time() + for field in upd_todo: + self._columns[field].set_memory(cr, self, id_new, field, vals[field], user, context) + self._validate(cr, user, [id_new], context) + wf_service = netsvc.LocalService("workflow") + wf_service.trg_write(user, self._name, id_new, cr) self.clear() - return super(cacheable_osv, self).unlink(cr, user, ids) + return id_new -#cacheable_osv = osv + def create(self, cr, user, vals, context=None): + self.next_id += 1 + id_new = self.next_id + default = [] + for f in self._columns.keys(): + if not f in vals: + default.append(f) + if len(default): + vals.update(self.default_get(cr, user, default, context)) + vals2 = {} + upd_todo = [] + for field in vals: + if self._columns[field]._classic_write: + vals2[field] = vals[field] + else: + upd_todo.append(field) + self.datas[id_new] = vals2 + self.datas[id_new]['internal.date_access'] = time.time() + for field in upd_todo: + self._columns[field].set_memory(cr, self, id_new, field, vals[field], user, context) + self._validate(cr, user, [id_new], context) + wf_service = netsvc.LocalService("workflow") + wf_service.trg_create(user, self._name, id_new, cr) + self.clear() + return id_new + + def default_get(self, cr, uid, fields_list, context=None): + if not context: + context = {} + value = {} + # get the default values for the inherited fields + for f in fields_list: + if f in self._defaults: + value[f] = self._defaults[f](self, cr, uid, context) + fld_def = ((f in self._columns) and self._columns[f]) \ + or ((f in self._inherit_fields) and self._inherit_fields[f][2]) \ + or False + + # get the default values set by the user and override the default + # values defined in the object + ir_values_obj = self.pool.get('ir.values') + res = ir_values_obj.get(cr, uid, 'default', False, [self._name]) + for id, field, field_value in res: + if field in fields_list: + fld_def = (field in self._columns) + if fld_def._type in ('many2one', 'one2one'): + obj = self.pool.get(fld_def._obj) + if not obj.search(cr, uid, [('id', '=', field_value)]): + continue + if fld_def._type in ('many2many'): + obj = self.pool.get(fld_def._obj) + field_value2 = [] + for i in range(len(field_value)): + if not obj.search(cr, uid, [('id', '=', + field_value[i])]): + continue + field_value2.append(field_value[i]) + field_value = field_value2 + if fld_def._type in ('one2many'): + obj = self.pool.get(fld_def._obj) + field_value2 = [] + for i in range(len(field_value)): + field_value2.append({}) + for field2 in field_value[i]: + if obj._columns[field2]._type in ('many2one', 'one2one'): + obj2 = self.pool.get(obj._columns[field2]._obj) + if not obj2.search(cr, uid, + [('id', '=', field_value[i][field2])]): + continue + # TODO add test for many2many and one2many + field_value2[i][field2] = field_value[i][field2] + field_value = field_value2 + value[field] = field_value + return value + + def search(self, cr, user, args, offset=0, limit=None, order=None, + context=None, count=False): + return self.datas.keys() + + def unlink(self, cr, uid, ids, context=None): + for id in ids: + if id in self.datas: + del self.datas[id] + if len(ids): + cr.execute('delete from wkf_instance where res_type=%s and res_id in ('+','.join(map(str, ids))+')', (self._name, )) + return True + + def perm_read(self, cr, user, ids, context=None, details=True): + result = [] + for id in ids: + result.append({ + 'create_uid': (user, 'Root'), + 'create_date': time.strftime('%Y-%m-%d %H:%M:%S'), + 'write_uid': False, + 'write_date': False, + 'id': id + }) + return result + +class orm(orm_template): + + _sql_constraints = [] + + _log_access = True + _table = None + _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'] + def _auto_init(self, cr, context={}): + logger = netsvc.Logger() + create = False + self._field_create(cr, context=context) + if not hasattr(self, "_auto") or self._auto: + cr.execute("SELECT relname FROM pg_class WHERE relkind in ('r','v') AND relname='%s'" % self._table) + if not cr.rowcount: + cr.execute("CREATE TABLE \"%s\" (id SERIAL NOT NULL, PRIMARY KEY(id)) WITH OIDS" % self._table) + create = True + cr.commit() + if self._log_access: + logs = { + 'create_uid': 'INTEGER REFERENCES res_users ON DELETE SET NULL', + 'create_date': 'TIMESTAMP', + 'write_uid': 'INTEGER REFERENCES res_users ON DELETE SET NULL', + 'write_date': 'TIMESTAMP' + } + for k in logs: + cr.execute( + """ + SELECT c.relname + FROM pg_class c, pg_attribute a + WHERE c.relname='%s' AND a.attname='%s' AND c.oid=a.attrelid + """ % (self._table, k)) + if not cr.rowcount: + cr.execute("ALTER TABLE \"%s\" ADD COLUMN \"%s\" %s" % + (self._table, k, logs[k])) + cr.commit() + + # iterate on the database columns to drop the NOT NULL constraints + # of fields which were required but have been removed + cr.execute( + "SELECT a.attname, a.attnotnull "\ + "FROM pg_class c, pg_attribute a "\ + "WHERE c.oid=a.attrelid AND c.relname='%s'" % self._table) + db_columns = cr.dictfetchall() + for column in db_columns: + if column['attname'] not in ('id', 'oid', 'tableoid', 'ctid', 'xmin', 'xmax', 'cmin', 'cmax'): + if column['attnotnull'] and column['attname'] not in self._columns: + cr.execute("ALTER TABLE \"%s\" ALTER COLUMN \"%s\" DROP NOT NULL" % (self._table, column['attname'])) + + # iterate on the "object columns" + 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,) + 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 cr.fetchone(): + cr.execute("SELECT count(*) 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] + if not res: + cr.execute("ALTER TABLE \"%s\" ADD FOREIGN KEY (%s) REFERENCES \"%s\" ON DELETE SET NULL" % (self._obj, f._fields_id, f._table)) + elif isinstance(f, fields.many2many): + cr.execute("SELECT relname FROM pg_class WHERE relkind in ('r','v') AND relname=%s", (f._rel,)) + if not cr.dictfetchall(): + #FIXME: Remove this try/except + try: + ref = self.pool.get(f._obj)._table + except AttributeError: + ref = f._obj.replace('.', '_') + cr.execute("CREATE TABLE \"%s\" (\"%s\" INTEGER NOT NULL REFERENCES \"%s\" ON DELETE CASCADE, \"%s\" INTEGER NOT NULL REFERENCES \"%s\" ON DELETE CASCADE) WITH OIDS"%(f._rel, f._id1, self._table, f._id2, ref)) + cr.execute("CREATE INDEX \"%s_%s_index\" ON \"%s\" (\"%s\")" % (f._rel, f._id1, f._rel, f._id1)) + cr.execute("CREATE INDEX \"%s_%s_index\" ON \"%s\" (\"%s\")" % (f._rel, f._id2, f._rel, f._id2)) + cr.commit() + else: + cr.execute("SELECT c.relname,a.attname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,t.typname,CASE WHEN a.attlen=-1 THEN a.atttypmod-4 ELSE a.attlen END as size FROM pg_class c,pg_attribute a,pg_type t WHERE c.relname=%s AND a.attname=%s AND c.oid=a.attrelid AND a.atttypid=t.oid", (self._table, k)) + res = cr.dictfetchall() + if not res: + if not isinstance(f, fields.function) or f.store: + + # add the missing field + cr.execute("ALTER TABLE \"%s\" ADD COLUMN \"%s\" %s" % (self._table, k, get_pg_type(f)[1])) + + # initialize it + if not create and k in self._defaults: + default = self._defaults[k](self, cr, 1, {}) + if not default: + cr.execute("UPDATE \"%s\" SET \"%s\"=NULL" % (self._table, k)) + else: + cr.execute("UPDATE \"%s\" SET \"%s\"='%s'" % (self._table, k, default)) + if isinstance(f, fields.function): + cr.execute('select id from '+self._table) + ids_lst = map(lambda x: x[0], cr.fetchall()) + while ids_lst: + iids = ids_lst[:40] + ids_lst = ids_lst[40:] + res = f.get(cr, self, iids, k, 1, {}) + for r in res.items(): + cr.execute("UPDATE \"%s\" SET \"%s\"='%s' where id=%d"% (self._table, k, r[1], r[0])) + + # and add constraints if needed + if isinstance(f, fields.many2one): + #FIXME: Remove this try/except + try: + ref = self.pool.get(f._obj)._table + except AttributeError: + ref = f._obj.replace('.', '_') + # ir_actions is inherited so foreign key doesn't work on it + if ref != 'ir_actions': + cr.execute("ALTER TABLE \"%s\" ADD FOREIGN KEY (\"%s\") REFERENCES \"%s\" ON DELETE %s" % (self._table, k, ref, f.ondelete)) + if f.select: + cr.execute("CREATE INDEX \"%s_%s_index\" ON \"%s\" (\"%s\")" % (self._table, k, self._table, k)) + if f.required: + cr.commit() + try: + cr.execute("ALTER TABLE \"%s\" ALTER COLUMN \"%s\" SET NOT NULL" % (self._table, k)) + except: + logger.notifyChannel('init', netsvc.LOG_WARNING, 'WARNING: unable to set column %s of table %s not null !\nTry to re-run: tinyerp-server.py --update=module\nIf it doesn\'t work, update records and execute manually:\nALTER TABLE %s ALTER COLUMN %s SET NOT NULL' % (k, self._table, self._table, k)) + cr.commit() + elif len(res)==1: + f_pg_def = res[0] + f_pg_type = f_pg_def['typname'] + f_pg_size = f_pg_def['size'] + f_pg_notnull = f_pg_def['attnotnull'] + if isinstance(f, fields.function) and not f.store: + logger.notifyChannel('init', netsvc.LOG_WARNING, 'column %s (%s) in table %s was converted to a function !\nYou should remove this column from your database.' % (k, f.string, self._table)) + f_obj_type = None + else: + f_obj_type = get_pg_type(f) and get_pg_type(f)[0] + + if f_obj_type: + if f_pg_type != f_obj_type: + logger.notifyChannel('init', netsvc.LOG_WARNING, "column '%s' in table '%s' has changed type (DB = %s, def = %s) !" % (k, self._table, f_pg_type, f._type)) + if f_pg_type == 'varchar' and f._type == 'char' and f_pg_size != f.size: + # columns with the name 'type' cannot be changed for an unknown reason?! + if k != 'type': + if f_pg_size > f.size: + logger.notifyChannel('init', netsvc.LOG_WARNING, "column '%s' in table '%s' has changed size (DB = %d, def = %d), DB size will be kept !" % (k, self._table, f_pg_size, f.size)) + # If actual DB size is < than new + # We update varchar size, otherwise, we keep DB size + # to avoid truncated string... + if f_pg_size < f.size: + cr.execute("ALTER TABLE \"%s\" RENAME COLUMN \"%s\" TO temp_change_size" % (self._table, k)) + cr.execute("ALTER TABLE \"%s\" ADD COLUMN \"%s\" VARCHAR(%d)" % (self._table, k, f.size)) + cr.execute("UPDATE \"%s\" SET \"%s\"=temp_change_size::VARCHAR(%d)" % (self._table, k, f.size)) + cr.execute("ALTER TABLE \"%s\" DROP COLUMN temp_change_size" % (self._table,)) + cr.commit() + # if the field is required and hasn't got a NOT NULL constraint + if f.required and f_pg_notnull == 0: + # set the field to the default value if any + if k in self._defaults: + default = self._defaults[k](self, cr, 1, {}) + if not (default is False): + cr.execute("UPDATE \"%s\" SET \"%s\"='%s' WHERE %s is NULL" % (self._table, k, default, k)) + cr.commit() + # add the NOT NULL constraint + try: + cr.execute("ALTER TABLE \"%s\" ALTER COLUMN \"%s\" SET NOT NULL" % (self._table, k)) + cr.commit() + except: + logger.notifyChannel('init', netsvc.LOG_WARNING, 'unable to set a NOT NULL constraint on column %s of the %s table !\nIf you want to have it, you should update the records and execute manually:\nALTER TABLE %s ALTER COLUMN %s SET NOT NULL' % (k, self._table, self._table, k)) + cr.commit() + elif not f.required and f_pg_notnull == 1: + cr.execute("ALTER TABLE \"%s\" ALTER COLUMN \"%s\" DROP NOT NULL" % (self._table, k)) + cr.commit() + cr.execute("SELECT indexname FROM pg_indexes WHERE indexname = '%s_%s_index' and tablename = '%s'" % (self._table, k, self._table)) + res = cr.dictfetchall() + if not res and f.select: + cr.execute("CREATE INDEX \"%s_%s_index\" ON \"%s\" (\"%s\")" % (self._table, k, self._table, k)) + cr.commit() + if res and not f.select: + cr.execute("DROP INDEX \"%s_%s_index\"" % (self._table, k)) + cr.commit() + if isinstance(f, fields.many2one): + ref = self.pool.get(f._obj)._table + if ref != 'ir_actions': + cr.execute('SELECT confdeltype, conname FROM pg_constraint as con, pg_class as cl1, pg_class as cl2, ' \ + 'pg_attribute as att1, pg_attribute as att2 ' \ + 'WHERE con.conrelid = cl1.oid ' \ + 'AND cl1.relname = %s ' \ + 'AND con.confrelid = cl2.oid ' \ + 'AND cl2.relname = %s ' \ + 'AND array_lower(con.conkey, 1) = 1 ' \ + 'AND con.conkey[1] = att1.attnum ' \ + 'AND att1.attrelid = cl1.oid ' \ + 'AND att1.attname = %s ' \ + 'AND array_lower(con.confkey, 1) = 1 ' \ + 'AND con.confkey[1] = att2.attnum ' \ + 'AND att2.attrelid = cl2.oid ' \ + 'AND att2.attname = %s ' \ + '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'): + 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() + else: + print "ERROR" + else: + cr.execute("SELECT relname FROM pg_class WHERE relkind in ('r','v') AND relname='%s'" % self._table) + create = not bool(cr.fetchone()) + + for (key, con, _) in self._sql_constraints: + cr.execute("SELECT conname FROM pg_constraint where conname='%s_%s'" % (self._table, key)) + if not cr.dictfetchall(): + try: + cr.execute('alter table \"%s\" add constraint \"%s_%s\" %s' % (self._table, self._table, key, con,)) + cr.commit() + except: + logger.notifyChannel('init', netsvc.LOG_WARNING, 'unable to add \'%s\' constraint on table %s !\n If you want to have it, you should update the records and execute manually:\nALTER table %s ADD CONSTRAINT %s_%s %s' % (con, self._table, self._table, self._table, key, con,)) + + if create: + if hasattr(self, "_sql"): + for line in self._sql.split(';'): + line2 = line.replace('\n', '').strip() + if line2: + cr.execute(line2) + cr.commit() + + def __init__(self, cr): + super(orm, self).__init__(cr) + f = filter(lambda a: isinstance(self._columns[a], fields.function) and self._columns[a].store, self._columns) + if f: + list_store = [] + tuple_store = () + tuple_fn = () + for store_field in f: + if not self._columns[store_field].store == True: + dict_store = self._columns[store_field].store + key = dict_store.keys() + list_data = [] + for i in key: + tuple_store = self._name, store_field, self._columns[store_field]._fnct.__name__, tuple(dict_store[i][0]), dict_store[i][1], i + list_data.append(tuple_store) + #tuple_store=self._name,store_field,self._columns[store_field]._fnct.__name__,tuple(dict_store[key[0]][0]),dict_store[key[0]][1] + for l in list_data: + list_store = [] + if l[5] in self.pool._store_function.keys(): + self.pool._store_function[l[5]].append(l) + temp_list = list(set(self.pool._store_function[l[5]])) + self.pool._store_function[l[5]] = temp_list + else: + list_store.append(l) + self.pool._store_function[l[5]] = list_store + + for (key, _, msg) in self._sql_constraints: + self.pool._sql_error[self._table+'_'+key] = msg + + # Load manual fields + + cr.execute("SELECT id FROM ir_model_fields WHERE name=%s AND model=%s", ('state', 'ir.model.fields')) + if cr.fetchone(): + cr.execute('SELECT * FROM ir_model_fields WHERE model=%s AND state=%s', (self._name, 'manual')) + for field in cr.dictfetchall(): + if field['name'] in self._columns: + continue + attrs = { + 'string': field['field_description'], + 'required': bool(field['required']), + 'readonly': bool(field['readonly']), + 'domain': field['domain'] or None, + 'size': field['size'], + 'ondelete': field['on_delete'], + 'translate': (field['translate']), + #'select': int(field['select_level']) + } + #if field['relation']: + # attrs['relation'] = field['relation'] + if field['ttype'] == 'selection': + self._columns[field['name']] = getattr(fields, field['ttype'])(eval(field['selection']), **attrs) + elif field['ttype'] == 'many2one': + self._columns[field['name']] = getattr(fields, field['ttype'])(field['relation'], **attrs) + else: + self._columns[field['name']] = getattr(fields, field['ttype'])(**attrs) + + self._inherits_reload() + if not self._sequence: + self._sequence = self._table+'_id_seq' + for k in self._defaults: + assert (k in self._columns) or (k in self._inherit_fields), 'Default function defined in %s but field %s does not exist !' % (self._name, k,) + for f in self._columns: + self._columns[f].restart() + + def default_get(self, cr, uid, fields_list, context=None): + if not context: + context = {} + value = {} + # get the default values for the inherited fields + for t in self._inherits.keys(): + value.update(self.pool.get(t).default_get(cr, uid, fields_list, + context)) + + # get the default values defined in the object + for f in fields_list: + if f in self._defaults: + value[f] = self._defaults[f](self, cr, uid, context) + fld_def = ((f in self._columns) and self._columns[f]) \ + or ((f in self._inherit_fields) and self._inherit_fields[f][2]) \ + or False + if isinstance(fld_def, fields.property): + property_obj = self.pool.get('ir.property') + definition_id = fld_def._field_get(cr, uid, self._name, f) + nid = property_obj.search(cr, uid, [('fields_id', '=', + definition_id), ('res_id', '=', False)]) + if nid: + prop_value = property_obj.browse(cr, uid, nid[0], + context=context).value + value[f] = (prop_value and int(prop_value.split(',')[1])) \ + or False + + # get the default values set by the user and override the default + # values defined in the object + ir_values_obj = self.pool.get('ir.values') + res = ir_values_obj.get(cr, uid, 'default', False, [self._name]) + for id, field, field_value in res: + if field in fields_list: + 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)]): + continue + if fld_def._type in ('many2many'): + obj = self.pool.get(fld_def._obj) + field_value2 = [] + for i in range(len(field_value)): + if not obj.search(cr, uid, [('id', '=', + field_value[i])]): + continue + field_value2.append(field_value[i]) + field_value = field_value2 + if fld_def._type in ('one2many'): + obj = self.pool.get(fld_def._obj) + field_value2 = [] + for i in range(len(field_value)): + field_value2.append({}) + for field2 in field_value[i]: + if obj._columns[field2]._type in ('many2one', 'one2one'): + obj2 = self.pool.get(obj._columns[field2]._obj) + if not obj2.search(cr, uid, + [('id', '=', field_value[i][field2])]): + continue + # TODO add test for many2many and one2many + field_value2[i][field2] = field_value[i][field2] + field_value = field_value2 + value[field] = field_value + return value -#class FakePool(object): -# def __init__(self, module): -# self.preferred_module = module + # + # 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: + obj._inherits_reload() -# def get(self, name): -# localpool = module_objects_dict.get(self.preferred_module, {'dict': {}})['dict'] -# if name in localpool: -# obj = localpool[name] -# else: -# obj = pooler.get_pool(cr.dbname).get(name) -# return obj + def _inherits_reload(self): + res = {} + for table in self._inherits: + res.update(self.pool.get(table)._inherit_fields) + for col in self.pool.get(table)._columns.keys(): + res[col] = (table, self._inherits[table], self.pool.get(table)._columns[col]) + for col in self.pool.get(table)._inherit_fields.keys(): + res[col] = (table, self._inherits[table], self.pool.get(table)._inherit_fields[col][2]) + self._inherit_fields = res + self._inherits_reload_src() -# fake_pool = self -# class fake_class(obj.__class__): -# def __init__(self): -# super(fake_class, self).__init__() -# self.pool = fake_pool + def fields_get(self, cr, user, fields=None, context=None): + read_access = self.pool.get('ir.model.access').check(cr, user, self._name, 'write', raise_exception=False) + return super(orm, self).fields_get(cr, user, fields, context, read_access) -# return fake_class() + def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'): + if not context: + context = {} + self.pool.get('ir.model.access').check(cr, user, self._name, 'read') + if not fields: + fields = self._columns.keys() + self._inherit_fields.keys() + select = ids + if isinstance(ids, (int, long)): + select = [ids] + result = self._read_flat(cr, user, select, fields, context, load) + for r in result: + for key, v in r.items(): + if v == None: + r[key] = False + if isinstance(ids, (int, long)): + return result[0] + return result + + def _read_flat(self, cr, user, ids, fields, context=None, load='_classic_read'): + if not context: + context = {} + if not ids: + return [] + + if fields == None: + fields = self._columns.keys() + + # construct a clause for the rules : + d1, d2 = self.pool.get('ir.rule').domain_get(cr, user, self._name) + + # all inherited fields + all non inherited fields for which the attribute whose name is in load is True + fields_pre = filter(lambda x: x in self._columns and getattr(self._columns[x], '_classic_write'), fields) + self._inherits.values() + + res = [] + if len(fields_pre): + fields_pre2 = map(lambda x: (x in ('create_date', 'write_date')) and ('date_trunc(\'second\', '+x+') as '+x) or '"'+x+'"', fields_pre) + for i in range(0, len(ids), cr.IN_MAX): + sub_ids = ids[i:i+cr.IN_MAX] + if d1: + cr.execute('SELECT %s FROM \"%s\" WHERE id IN (%s) AND %s ORDER BY %s' % \ + (','.join(fields_pre2 + ['id']), self._table, + ','.join([str(x) for x in sub_ids]), d1, + self._order), d2) + if not cr.rowcount == len({}.fromkeys(sub_ids)): + raise except_orm(_('AccessError'), + _('You try to bypass an access rule (Document type: %s).') % self._description) + else: + cr.execute('SELECT %s FROM \"%s\" WHERE id IN (%s) ORDER BY %s' % \ + (','.join(fields_pre2 + ['id']), self._table, + ','.join([str(x) for x in sub_ids]), + self._order)) + res.extend(cr.dictfetchall()) + else: + res = map(lambda x: {'id': x}, ids) + + for f in fields_pre: + if self._columns[f].translate: + ids = map(lambda x: x['id'], res) + res_trans = self.pool.get('ir.translation')._get_ids(cr, user, self._name+','+f, 'model', context.get('lang', False) or 'en_US', ids) + for r in res: + r[f] = res_trans.get(r['id'], False) or r[f] + + for table in self._inherits: + col = self._inherits[table] + cols = intersect(self._inherit_fields.keys(), fields) + if not cols: + continue + res2 = self.pool.get(table).read(cr, user, [x[col] for x in res], cols, context, load) + + res3 = {} + for r in res2: + res3[r['id']] = r + del r['id'] + + for record in res: + record.update(res3[record[col]]) + if col not in fields: + del record[col] + + # 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) + 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]) + ids = map(lambda x: x['id'], res) + + # all non inherited fields for which the attribute whose name is in load is False + fields_post = filter(lambda x: x in self._columns and not getattr(self._columns[x], load), fields) + for f in fields_post: + # get the value of that field for all records/ids + res2 = self._columns[f].get(cr, self, ids, f, user, context=context, values=res) + for record in res: + record[f] = res2[record['id']] + + readonly = None + for vals in res: + for field in vals.copy(): + fobj = None + if field in self._columns: + fobj = self._columns[field] + + if not fobj: + continue + groups = fobj.read + if groups: + edit = False + for group in groups: + module = group.split(".")[0] + grp = group.split(".")[1] + 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" % \ + (grp, module, 'res.groups', user)) + readonly = cr.fetchall() + if readonly[0][0] >= 1: + edit = True + break + elif readonly[0][0] == 0: + edit = False + else: + edit = False + + if not edit: + if type(vals[field]) == type([]): + vals[field] = [] + elif type(vals[field]) == type(0.0): + vals[field] = 0 + elif type(vals[field]) == type(''): + vals[field] = '=No Permission=' + else: + vals[field] = False + return res + + def perm_read(self, cr, user, ids, context=None, details=True): + if not context: + context = {} + if not ids: + return [] + fields = '' + if self._log_access: + fields = ', u.create_uid, u.create_date, u.write_uid, u.write_date' + if isinstance(ids, (int, long)): + ids_str = str(ids) + else: + ids_str = ','.join([str(i) for i in ids]) + cr.execute('select u.id'+fields+' from "'+self._table+'" u where u.id in ('+ids_str+')') + res = cr.dictfetchall() + for r in res: + for key in r: + r[key] = r[key] or False + if key in ('write_uid', 'create_uid', 'uid') and details: + if r[key]: + r[key] = self.pool.get('res.users').name_get(cr, user, [r[key]])[0] + if isinstance(ids, (int, long)): + return res[ids] + return res + + def unlink(self, cr, uid, ids, context=None): + if not context: + context = {} + if not ids: + return True + if isinstance(ids, (int, long)): + ids = [ids] + + fn_list = [] + if self._name in self.pool._store_function.keys(): + list_store = self.pool._store_function[self._name] + fn_data = () + id_change = [] + for tuple_fn in list_store: + for id in ids: + id_change.append(self._store_get_ids(cr, uid, id, tuple_fn, context)[0]) + fn_data = id_change, tuple_fn + fn_list.append(fn_data) + + delta = context.get('read_delta', False) + if delta and self._log_access: + for i in range(0, len(ids), cr.IN_MAX): + sub_ids = ids[i:i+cr.IN_MAX] + cr.execute("select (now() - min(write_date)) <= '%s'::interval " \ + "from \"%s\" where id in (%s)" % + (delta, self._table, ",".join(map(str, sub_ids)))) + res = cr.fetchone() + if res and res[0]: + raise except_orm(_('ConcurrencyException'), + _('This record was modified in the meanwhile')) + + self.pool.get('ir.model.access').check(cr, uid, self._name, 'unlink') + + wf_service = netsvc.LocalService("workflow") + for id in ids: + wf_service.trg_delete(uid, self._name, id, cr) + + #cr.execute('select * from '+self._table+' where id in ('+str_d+')', ids) + #res = cr.dictfetchall() + #for key in self._inherits: + # ids2 = [x[self._inherits[key]] for x in res] + # self.pool.get(key).unlink(cr, uid, ids2) + + d1, d2 = self.pool.get('ir.rule').domain_get(cr, uid, self._name) + if d1: + d1 = ' AND '+d1 + + for i in range(0, len(ids), cr.IN_MAX): + sub_ids = ids[i:i+cr.IN_MAX] + ','.join(('%d',)*len(sub_ids)) + if d1: + cr.execute('SELECT id FROM "'+self._table+'" ' \ + 'WHERE id IN ('+str_d+')'+d1, sub_ids+d2) + if not cr.rowcount == len({}.fromkeys(ids)): + raise except_orm(_('AccessError'), + _('You try to bypass an access rule (Document type: %s).') % \ + self._description) + + if d1: + cr.execute('delete from "'+self._table+'" ' \ + 'where id in ('+str_d+')'+d1, sub_ids+d2) + else: + cr.execute('delete from "'+self._table+'" ' \ + 'where id in ('+str_d+')', sub_ids) + if fn_list: + for ids, tuple_fn in fn_list: + self._store_set_values(cr, uid, ids, tuple_fn, id_change, context) + + return True + + # + # TODO: Validate + # + def write(self, cr, user, ids, vals, context=None): + readonly = None + for field in vals.copy(): + fobj = None + if field in self._columns: + fobj = self._columns[field] + else: + fobj = self._inherit_fields[field][2] + if not fobj: + continue + groups = fobj.write + + if groups: + edit = False + for group in groups: + module = group.split(".")[0] + grp = group.split(".")[1] + 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" % \ + (grp, module, 'res.groups', user)) + readonly = cr.fetchall() + if readonly[0][0] >= 1: + edit = True + break + elif readonly[0][0] == 0: + edit = False + else: + edit = False + + if not edit: + vals.pop(field) + + if not context: + context = {} + if not ids: + return True + if isinstance(ids, (int, long)): + ids = [ids] + delta = context.get('read_delta', False) + if delta and self._log_access: + for i in range(0, len(ids), cr.IN_MAX): + sub_ids = ids[i:i+cr.IN_MAX] + cr.execute("select (now() - min(write_date)) <= '%s'::interval " \ + "from %s where id in (%s)" % + (delta, self._table, ",".join(map(str, sub_ids)))) + res = cr.fetchone() + if res and res[0]: + for field in vals: + if field in self._columns and self._columns[field]._classic_write: + raise except_orm(_('ConcurrencyException'), + _('This record was modified in the meanwhile')) + + self.pool.get('ir.model.access').check(cr, user, self._name, 'write') + + #for v in self._inherits.values(): + # assert v not in vals, (v, vals) + upd0 = [] + upd1 = [] + upd_todo = [] + updend = [] + direct = [] + totranslate = context.get('lang', False) and (context['lang'] != 'en_US') + for field in vals: + if field in self._columns: + if self._columns[field]._classic_write: + if (not totranslate) or not self._columns[field].translate: + upd0.append('"'+field+'"='+self._columns[field]._symbol_set[0]) + upd1.append(self._columns[field]._symbol_set[1](vals[field])) + direct.append(field) + else: + upd_todo.append(field) + else: + updend.append(field) + if field in self._columns \ + and hasattr(self._columns[field], 'selection') \ + and vals[field]: + if self._columns[field]._type == 'reference': + val = vals[field].split(',')[0] + else: + val = vals[field] + if isinstance(self._columns[field].selection, (tuple, list)): + if val not in dict(self._columns[field].selection): + raise except_orm(_('ValidateError'), + _('The value "%s" for the field "%s" is not in the selection') \ + % (vals[field], field)) + else: + if val not in dict(self._columns[field].selection( + self, cr, user, context=context)): + raise except_orm(_('ValidateError'), + _('The value "%s" for the field "%s" is not in the selection') \ + % (vals[field], field)) + + if self._log_access: + upd0.append('write_uid=%d') + upd0.append('write_date=now()') + upd1.append(user) + + if len(upd0): + + d1, d2 = self.pool.get('ir.rule').domain_get(cr, user, self._name) + if d1: + d1 = ' and '+d1 + + for i in range(0, len(ids), cr.IN_MAX): + sub_ids = ids[i:i+cr.IN_MAX] + ids_str = ','.join(map(str, sub_ids)) + if d1: + cr.execute('SELECT id FROM "'+self._table+'" ' \ + 'WHERE 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 (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) + if d1: + cr.execute('update "'+self._table+'" set ' + ','.join(upd0) + ' ' \ + 'where id in ('+ids_str+')'+d1, upd1+ d2) + else: + cr.execute('update "'+self._table+'" set ' + ','.join(upd0) + ' ' \ + 'where id in ('+ids_str+')', upd1) + + if totranslate: + for f in direct: + if self._columns[f].translate: + self.pool.get('ir.translation')._set_ids(cr, user, self._name+','+f, 'model', context['lang'], ids, vals[f]) + + # call the 'set' method of fields which are not classic_write + upd_todo.sort(lambda x, y: self._columns[x].priority-self._columns[y].priority) + for field in upd_todo: + for id in ids: + self._columns[field].set(cr, self, id, field, vals[field], user, context=context) + + 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 = ','.join(map(str, sub_ids)) + cr.execute('select distinct "'+col+'" from "'+self._table+'" ' \ + 'where id in ('+ids_str+')', upd1) + nids.extend([x[0] for x in cr.fetchall()]) + + v = {} + for val in updend: + if self._inherit_fields[val][0] == table: + v[val] = vals[val] + self.pool.get(table).write(cr, user, nids, v, context) + + self._validate(cr, user, ids, context) + + if 'read_delta' in context: + del context['read_delta'] + + wf_service = netsvc.LocalService("workflow") + for id in ids: + wf_service.trg_write(user, self._name, id, cr) + self._update_function_stored(cr, user, ids, context=context) + + if self._name in self.pool._store_function.keys(): + list_store = self.pool._store_function[self._name] + for tuple_fn in list_store: + flag = False + if not tuple_fn[3]: + flag = True + for field in tuple_fn[3]: + if field in vals.keys(): + flag = True + break + if flag: + id_change = self._store_get_ids(cr, user, ids[0], tuple_fn, context) + self._store_set_values(cr, user, ids[0], tuple_fn, id_change, context) + + return True + + # + # TODO: Should set perm to user.xxx + # + def create(self, cr, user, vals, context=None): + """ create(cr, user, vals, context) -> int + cr = database cursor + user = user id + vals = dictionary of the form {'field_name':field_value, ...} + """ + if not context: + context = {} + self.pool.get('ir.model.access').check(cr, user, self._name, 'create') + + default = [] + + avoid_table = [] + for (t, c) in self._inherits.items(): + if c in vals: + avoid_table.append(t) + for f in self._columns.keys(): # + self._inherit_fields.keys(): + if not f in vals: + default.append(f) + for f in self._inherit_fields.keys(): + if (not f in vals) and (not self._inherit_fields[f][0] in avoid_table): + default.append(f) + + if len(default): + vals.update(self.default_get(cr, user, default, context)) + + tocreate = {} + for v in self._inherits: + if self._inherits[v] not in vals: + tocreate[v] = {} + + (upd0, upd1, upd2) = ('', '', []) + upd_todo = [] + + for v in vals.keys(): + if v in self._inherit_fields: + (table, col, col_detail) = self._inherit_fields[v] + tocreate[table][v] = vals[v] + del vals[v] + + cr.execute("SELECT nextval('"+self._sequence+"')") + id_new = cr.fetchone()[0] + for table in tocreate: + id = self.pool.get(table).create(cr, user, tocreate[table]) + upd0 += ','+self._inherits[table] + upd1 += ',%d' + upd2.append(id) + + for field in vals: + if self._columns[field]._classic_write: + upd0 = upd0 + ',"' + field+'"' + upd1 = upd1 + ',' + self._columns[field]._symbol_set[0] + upd2.append(self._columns[field]._symbol_set[1](vals[field])) + else: + upd_todo.append(field) + if field in self._columns \ + and hasattr(self._columns[field], 'selection') \ + and vals[field]: + if self._columns[field]._type == 'reference': + val = vals[field].split(',')[0] + else: + val = vals[field] + if isinstance(self._columns[field].selection, (tuple, list)): + if val not in dict(self._columns[field].selection): + raise except_orm(_('ValidateError'), + _('The value "%s" for the field "%s" is not in the selection') \ + % (vals[field], field)) + else: + if val not in dict(self._columns[field].selection( + self, cr, user, context=context)): + raise except_orm(_('ValidateError'), + _('The value "%s" for the field "%s" is not in the selection') \ + % (vals[field], field)) + if self._log_access: + upd0 += ',create_uid,create_date' + upd1 += ',%d,now()' + upd2.append(user) + cr.execute('insert into "'+self._table+'" (id'+upd0+") values ("+str(id_new)+upd1+')', tuple(upd2)) + upd_todo.sort(lambda x, y: self._columns[x].priority-self._columns[y].priority) + for field in upd_todo: + self._columns[field].set(cr, self, id_new, field, vals[field], user, context) + + self._validate(cr, user, [id_new], context) + + wf_service = netsvc.LocalService("workflow") + wf_service.trg_create(user, self._name, id_new, cr) + self._update_function_stored(cr, user, [id_new], context=context) + if self._name in self.pool._store_function.keys(): + list_store = self.pool._store_function[self._name] + for tuple_fn in list_store: + id_change = self._store_get_ids(cr, user, id_new, tuple_fn, context) + self._store_set_values(cr, user, id_new, tuple_fn, id_change, context) + + return id_new + + def _store_get_ids(self, cr, uid, ids, tuple_fn, context): + parent_id = getattr(self.pool.get(tuple_fn[0]), tuple_fn[4].func_name)(cr, uid, [ids]) + return parent_id + + def _store_set_values(self, cr, uid, ids, tuple_fn, parent_id, context): + name = tuple_fn[1] + table = tuple_fn[0] + args = {} + vals_tot = getattr(self.pool.get(table), tuple_fn[2])(cr, uid, parent_id, name, args, context) + write_dict = {} + for id in vals_tot.keys(): + write_dict[name] = vals_tot[id] + self.pool.get(table).write(cr, uid, [id], write_dict) + return True + + def _update_function_stored(self, cr, user, ids, context=None): + if not context: + context = {} + f = filter(lambda a: isinstance(self._columns[a], fields.function) \ + and self._columns[a].store, self._columns) + if f: + result = self.read(cr, user, ids, fields=f, context=context) + for res in result: + upd0 = [] + upd1 = [] + for field in res: + if field not in f: + continue + value = res[field] + if self._columns[field]._type in ('many2one', 'one2one'): + value = res[field][0] + upd0.append('"'+field+'"='+self._columns[field]._symbol_set[0]) + upd1.append(self._columns[field]._symbol_set[1](value)) + upd1.append(res['id']) + cr.execute('update "' + self._table + '" set ' + \ + ','.join(upd0) + ' where id = %d', upd1) + return True + + # + # TODO: Validate + # + def perm_write(self, cr, user, ids, fields, context=None): + raise _('This method does not exist anymore') + + # TODO: ameliorer avec NULL + def _where_calc(self, cr, user, args, active_test=True, context=None): + if not context: + context = {} + args = args[:] + # if the object has a field named 'active', filter out all inactive + # records unless they were explicitely asked for + if 'active' in self._columns and (active_test and context.get('active_test', True)): + args = [('&', ('active', '=', 1), tuple(args))] + + if args: + import expression + e = expression.expression(args) + e.parse(cr, user, self, context) + tables = e.get_tables() + qu1, qu2 = e.to_sql() + qu1 = qu1 and [qu1] or [] + else: + qu1, qu2, tables = [], [], ['"%s"' % self._table] + + return (qu1, qu2, tables) + + def _check_qorder(self, word): + if not regex_order.match(word): + raise except_orm(_('AccessError'), _('Bad query.')) + return True + + def search(self, cr, user, args, offset=0, limit=None, order=None, + context=None, count=False): + if not context: + context = {} + # compute the where, order by, limit and offset clauses + (qu1, qu2, tables) = self._where_calc(cr, user, args, context=context) + + if len(qu1): + qu1 = ' where ' + ' and '.join(qu1) + else: + qu1 = '' + + if order: + self._check_qorder(order) + order_by = order or self._order + + limit_str = limit and ' limit %d' % limit or '' + offset_str = offset and ' offset %d' % offset or '' + + + # construct a clause for the rules : + d1, d2 = self.pool.get('ir.rule').domain_get(cr, user, self._name) + if d1: + qu1 = qu1 and qu1+' and '+d1 or ' where '+d1 + qu2 += d2 + + if count: + cr.execute('select count(%s.id) from ' % self._table + + ','.join(tables) +qu1 + limit_str + offset_str, qu2) + res = cr.fetchall() + return res[0][0] + # execute the "main" query to fetch the ids we were searching for + cr.execute('select %s.id from ' % self._table + ','.join(tables) +qu1+' order by '+order_by+limit_str+offset_str, qu2) + res = cr.fetchall() + return [x[0] for x in res] + + # returns the different values ever entered for one field + # this is used, for example, in the client when the user hits enter on + # a char field + def distinct_field_get(self, cr, uid, field, value, args=None, offset=0, limit=None): + if not args: + args = [] + if field in self._inherit_fields: + return self.pool.get(self._inherit_fields[field][0]).distinct_field_get(cr, uid, field, value, args, offset, limit) + else: + return self._columns[field].search(cr, self, args, field, value, offset, limit, uid) + + def name_get(self, cr, user, ids, context=None): + if not context: + context = {} + if not ids: + return [] + if isinstance(ids, (int, long)): + ids = [ids] + return [(r['id'], str(r[self._rec_name])) for r in self.read(cr, user, ids, + [self._rec_name], context, load='_classic_write')] + + def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=80): + if not args: + args = [] + if not context: + context = {} + args = args[:] + if name: + args += [(self._rec_name, operator, name)] + ids = self.search(cr, user, args, limit=limit, context=context) + res = self.name_get(cr, user, ids, context) + return res + + def copy(self, cr, uid, id, default=None, context=None): + if not context: + context = {} + if not default: + default = {} + if 'state' not in default: + if 'state' in self._defaults: + default['state'] = self._defaults['state'](self, cr, uid, context) + data = self.read(cr, uid, [id], context=context)[0] + fields = self.fields_get(cr, uid) + for f in fields: + ftype = fields[f]['type'] + + if self._log_access and f in ('create_date', 'create_uid', 'write_date', 'write_uid'): + del data[f] + + if f in default: + data[f] = default[f] + elif ftype == 'function': + del data[f] + elif ftype == 'many2one': + try: + data[f] = data[f] and data[f][0] + except: + pass + elif ftype in ('one2many', 'one2one'): + res = [] + rel = self.pool.get(fields[f]['relation']) + for rel_id in data[f]: + # the lines are first duplicated using the wrong (old) + # parent but then are reassigned to the correct one thanks + # to the (4, ...) + res.append((4, rel.copy(cr, uid, rel_id, context=context))) + data[f] = res + elif ftype == 'many2many': + data[f] = [(6, 0, data[f])] + del data['id'] + for v in self._inherits: + del data[self._inherits[v]] + return self.create(cr, uid, data) + + def read_string(self, cr, uid, id, langs, fields=None, context=None): + if not context: + context = {} + res = {} + res2 = {} + self.pool.get('ir.model.access').check(cr, uid, 'ir.translation', 'read') + if not fields: + fields = self._columns.keys() + self._inherit_fields.keys() + for lang in langs: + res[lang] = {'code': lang} + for f in fields: + if f in self._columns: + res_trans = self.pool.get('ir.translation')._get_source(cr, uid, self._name+','+f, 'field', lang) + if res_trans: + res[lang][f] = res_trans + else: + res[lang][f] = self._columns[f].string + for table in self._inherits: + cols = intersect(self._inherit_fields.keys(), fields) + res2 = self.pool.get(table).read_string(cr, uid, id, langs, cols, context) + for lang in res2: + if lang in res: + res[lang] = {'code': lang} + for f in res2[lang]: + res[lang][f] = res2[lang][f] + return res + + def write_string(self, cr, uid, id, langs, vals, context=None): + if not context: + context = {} + self.pool.get('ir.model.access').check(cr, uid, 'ir.translation', 'write') + for lang in langs: + for field in vals: + if field in self._columns: + self.pool.get('ir.translation')._set_ids(cr, uid, self._name+','+field, 'field', lang, [0], vals[field]) + for table in self._inherits: + cols = intersect(self._inherit_fields.keys(), vals) + if cols: + self.pool.get(table).write_string(cr, uid, id, langs, vals, context) + return True + + def check_recursion(self, cr, uid, ids, parent=None): + if not parent: + parent = self._parent_name + ids_parent = ids[:] + while len(ids_parent): + ids_parent2 = [] + for i in range(0, len(ids), cr.IN_MAX): + sub_ids_parent = ids_parent[i:i+cr.IN_MAX] + cr.execute('SELECT distinct "'+parent+'"'+ + ' FROM "'+self._table+'" ' \ + 'WHERE id in ('+','.join(map(str, sub_ids_parent))+')') + ids_parent2.extend(filter(None, map(lambda x: x[0], cr.fetchall()))) + ids_parent = ids_parent2 + for i in ids_parent: + if i in ids: + return False + return True # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/bin/pooler.py b/bin/pooler.py index 4f47a1f158b..e5c5b82be98 100644 --- a/bin/pooler.py +++ b/bin/pooler.py @@ -36,6 +36,7 @@ import netsvc db_dic = {} pool_dic = {} + def get_db_and_pool(db_name, force_demo=False, status=None, update_module=False): if not status: status={} @@ -46,7 +47,7 @@ def get_db_and_pool(db_name, force_demo=False, status=None, update_module=False) logger.notifyChannel('pooler', netsvc.LOG_INFO, 'Connecting to %s' % (db_name)) db = sql_db.db_connect(db_name) db_dic[db_name] = db - + if db_name in pool_dic: pool = pool_dic[db_name] else: @@ -60,11 +61,13 @@ def get_db_and_pool(db_name, force_demo=False, status=None, update_module=False) pool.get('ir.cron')._poolJobs(db.dbname) return db, pool + def restart_pool(db_name, force_demo=False, update_module=False): # del db_dic[db_name] del pool_dic[db_name] return get_db_and_pool(db_name, force_demo, update_module=update_module) + def close_db(db_name): if db_name in db_dic: db_dic[db_name].truedb.close() @@ -72,6 +75,7 @@ def close_db(db_name): if db_name in pool_dic: del pool_dic[db_name] + def get_db_only(db_name): if db_name in db_dic: db = db_dic[db_name] @@ -80,10 +84,12 @@ def get_db_only(db_name): db_dic[db_name] = db return db + def get_db(db_name): # print "get_db", db_name return get_db_and_pool(db_name)[0] + def get_pool(db_name, force_demo=False, status=None, update_module=False): # print "get_pool", db_name pool = get_db_and_pool(db_name, force_demo, status, update_module)[1] @@ -94,6 +100,7 @@ def get_pool(db_name, force_demo=False, status=None, update_module=False): return pool # return get_db_and_pool(db_name)[1] + def init(): global db # db = get_db_only(tools.config['db_name']) diff --git a/bin/tinyerp-server.py b/bin/tinyerp-server.py index 3bef58fb0c9..969cd507965 100755 --- a/bin/tinyerp-server.py +++ b/bin/tinyerp-server.py @@ -50,7 +50,7 @@ __builtin__.__dict__['tinyerp_version_string'] = "Tiny ERP Server " + __version_ #---------------------------------------------------------- # python imports #---------------------------------------------------------- -import sys,os,signal +import sys, os, signal #---------------------------------------------------------- # ubuntu 8.04 has obsoleted `pyxml` package and installs here. # the path needs to be updated before any `import xml` @@ -75,9 +75,9 @@ logger = netsvc.Logger() import tools import time -if sys.platform=='win32': +if sys.platform == 'win32': import mx.DateTime - mx.DateTime.strptime = lambda x,y: mx.DateTime.mktime(time.strptime(x, y)) + mx.DateTime.strptime = lambda x, y: mx.DateTime.mktime(time.strptime(x, y)) #os.chdir(tools.file_path_root) @@ -104,9 +104,9 @@ try: except psycopg.OperationalError, err: logger.notifyChannel("init", netsvc.LOG_ERROR, "could not connect to database '%s'!" % (tools.config["db_name"],)) - msg = str(err).replace("FATAL:","").strip() + msg = str(err).replace("FATAL:", "").strip() db_msg = "database \"%s\" does not exist" % (tools.config["db_name"],) - + # Note: this is ugly but since psycopg only uses one exception for all errors # I don't think it's possible to do differently if msg == db_msg: @@ -165,7 +165,7 @@ if tools.config['upgrade']: tools.config['update'][m] = 1 #---------------------------------------------------------- -# import basic modules +# import basic modules #---------------------------------------------------------- import osv, workflow, report, service @@ -190,7 +190,7 @@ if tools.config["translate_out"]: buf = file(tools.config["translate_out"], "w") tools.trans_export(tools.config["language"], tools.config["translate_modules"], buf, fileformat) buf.close() - + logger.notifyChannel("init", netsvc.LOG_INFO, 'translation file written succesfully') sys.exit(0) @@ -217,12 +217,12 @@ if tools.config['xmlrpc']: sys.exit(1) interface = tools.config["interface"] secure = tools.config["secure"] - - httpd = netsvc.HttpDaemon(interface,port, secure) - + + httpd = netsvc.HttpDaemon(interface, port, secure) + if tools.config["xmlrpc"]: xml_gw = netsvc.xmlrpc.RpcGateway('web-services') - httpd.attach("/xmlrpc", xml_gw ) + httpd.attach("/xmlrpc", xml_gw) logger.notifyChannel("web-services", netsvc.LOG_INFO, "starting XML-RPC" + \ (tools.config['secure'] and ' Secure' or '') + \ @@ -242,10 +242,11 @@ if tools.config['netrpc']: logger.notifyChannel("init", netsvc.LOG_ERROR, "invalid port '%s'!" % (tools.config["netport"],)) sys.exit(1) netinterface = tools.config["netinterface"] - + tinySocket = netsvc.TinySocketServerThread(netinterface, netport, False) logger.notifyChannel("web-services", netsvc.LOG_INFO, "starting netrpc service, port "+str(netport)) + def handler(signum, frame): from tools import config if tools.config['netrpc']: @@ -259,8 +260,8 @@ def handler(signum, frame): from tools import config if config['pidfile']: - fd=open(config['pidfile'], 'w') - pidtext="%d" % (os.getpid()) + fd = open(config['pidfile'], 'w') + pidtext = "%d" % (os.getpid()) fd.write(pidtext) fd.close()