1 ##############################################################################
3 # Copyright (c) 2004-2008 Tiny SPRL (http://tiny.be) All Rights Reserved.
7 # WARNING: This program as such is intended to be used by professional
8 # programmers who take the whole responsability of assessing all potential
9 # consequences resulting from its eventual inadequacies and bugs
10 # End users who are looking for a ready-to-use solution with commercial
11 # garantees and support are strongly adviced to contract a Free Software
14 # This program is Free Software; you can redistribute it and/or
15 # modify it under the terms of the GNU General Public License
16 # as published by the Free Software Foundation; either version 2
17 # of the License, or (at your option) any later version.
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
24 # You should have received a copy of the GNU General Public License
25 # along with this program; if not, write to the Free Software
26 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 ###############################################################################
30 # OSV: Objects Services
40 from netsvc import Logger, LOG_ERROR
41 from tools.misc import UpdateableDict
44 module_class_list = {}
47 class except_osv(Exception):
48 def __init__(self, name, value, exc_type='warning'):
50 self.exc_type = exc_type
52 self.args = (exc_type,name)
54 class osv_pool(netsvc.Service):
58 self.module_object_list = {}
61 self._store_function = {}
62 netsvc.Service.__init__(self, 'object_proxy', audience='')
63 self.joinGroup('web-services')
64 self.exportMethod(self.exportedMethods)
65 self.exportMethod(self.obj_list)
66 self.exportMethod(self.exec_workflow)
67 self.exportMethod(self.execute)
68 self.exportMethod(self.execute_cr)
70 def execute_cr(self, cr, uid, obj, method, *args, **kw):
72 object = pooler.get_pool(cr.dbname).get(obj)
74 self.abortResponse(1, 'Object Error', 'warning',
75 'Object %s doesn\'t exist' % str(obj))
76 return getattr(object,method)(cr, uid, *args, **kw)
77 except orm.except_orm, inst:
78 self.abortResponse(1, inst.name, 'warning', inst.value)
79 except except_osv, inst:
80 self.abortResponse(1, inst.name, inst.exc_type, inst.value)
81 except psycopg.IntegrityError, inst:
82 for key in self._sql_error.keys():
84 self.abortResponse(1, 'Constraint Error', 'warning',
86 self.abortResponse(1, 'Integrity Error', 'warning', inst[0])
89 tb_s = reduce(lambda x, y: x+y, traceback.format_exception(
90 sys.exc_type, sys.exc_value, sys.exc_traceback))
92 logger.notifyChannel("web-services", LOG_ERROR,
93 'Exception in call: ' + tb_s)
96 def execute(self, db, uid, obj, method, *args, **kw):
97 db, pool = pooler.get_db_and_pool(db)
101 res = pool.execute_cr(cr, uid, obj, method, *args, **kw)
110 def exec_workflow_cr(self, cr, uid, obj, method, *args):
111 wf_service = netsvc.LocalService("workflow")
112 return wf_service.trg_validate(uid, obj, args[0], method, cr)
114 def exec_workflow(self, db, uid, obj, method, *args):
115 cr = pooler.get_db(db).cursor()
118 res = self.exec_workflow_cr(cr, uid, obj, method, *args)
120 except orm.except_orm, inst:
122 self.abortResponse(1, inst.name, 'warning', inst.value)
123 except except_osv, inst:
125 self.abortResponse(1, inst.name, inst[0], inst.value)
131 return self.obj_pool.keys()
133 # adds a new object instance to the object pool.
134 # if it already existed, the instance is replaced
135 def add(self, name, obj_inst):
136 if self.obj_pool.has_key(name):
137 del self.obj_pool[name]
138 self.obj_pool[name] = obj_inst
140 module = str(obj_inst.__class__)[6:]
141 module = module[:len(module)-1]
142 module = module.split('.')[0][2:]
143 self.module_object_list.setdefault(module, []).append(obj_inst)
146 obj = self.obj_pool.get(name, None)
147 # We cannot uncomment this line because it breaks initialisation since objects do not initialize
148 # in the correct order and the ORM doesnt support correctly when some objets do not exist yet
149 # assert obj, "object %s does not exist !" % name
152 #TODO: pass a list of modules to load
153 def instanciate(self, module, cr):
154 # print "module list:", module_list
155 # for module in module_list:
157 class_list = module_class_list.get(module, [])
158 # if module not in self.module_object_list:
159 # print "%s class_list:" % module, class_list
160 for klass in class_list:
161 res.append(klass.createInstance(self, module, cr))
164 # print "skipping module", module
166 #pooler.get_pool(cr.dbname) = osv_pool()
168 class osv_memory(orm.orm_memory):
169 #__metaclass__ = inheritor
171 module = str(cls)[6:]
172 module = module[:len(module)-1]
173 module = module.split('.')[0][2:]
174 if not hasattr(cls, '_module'):
176 module_class_list.setdefault(cls._module, []).append(cls)
177 class_pool[cls._name] = cls
178 if module not in module_list:
179 module_list.append(cls._module)
183 # Goal: try to apply inheritancy at the instanciation level and
184 # put objects in the pool var
186 def createInstance(cls, pool, module, cr):
187 name = hasattr(cls,'_name') and cls._name or cls._inherit
188 parent_name = hasattr(cls, '_inherit') and cls._inherit
190 print 'Inherit not supported in osv_memory object !'
191 obj = object.__new__(cls)
192 obj.__init__(pool, cr)
194 createInstance = classmethod(createInstance)
196 def __init__(self, pool, cr):
197 pool.add(self._name, self)
199 orm.orm_memory.__init__(self, cr)
204 #__metaclass__ = inheritor
206 module = str(cls)[6:]
207 module = module[:len(module)-1]
208 module = module.split('.')[0][2:]
209 if not hasattr(cls, '_module'):
211 module_class_list.setdefault(cls._module, []).append(cls)
212 class_pool[cls._name] = cls
213 if module not in module_list:
214 module_list.append(cls._module)
218 # Goal: try to apply inheritancy at the instanciation level and
219 # put objects in the pool var
221 def createInstance(cls, pool, module, cr):
222 parent_name = hasattr(cls, '_inherit') and cls._inherit
224 parent_class = pool.get(parent_name).__class__
225 assert parent_class, "parent class %s does not exist !" % parent_name
227 for s in ('_columns', '_defaults', '_inherits', '_constraints', '_sql_constraints'):
228 new = copy.copy(getattr(pool.get(parent_name), s))
229 if hasattr(new, 'update'):
230 new.update(cls.__dict__.get(s, {}))
232 new.extend(cls.__dict__.get(s, []))
234 name = hasattr(cls,'_name') and cls._name or cls._inherit
235 cls = type(name, (cls, parent_class), nattr)
236 obj = object.__new__(cls)
237 obj.__init__(pool, cr)
239 createInstance = classmethod(createInstance)
241 def __init__(self, pool, cr):
242 pool.add(self._name, self)
244 orm.orm.__init__(self, cr)
246 class Cacheable(object):
248 _cache = UpdateableDict()
250 def add(self, key, value):
251 self._cache[key] = value
253 def invalidate(self, key):
267 def filter_dict(d, fields):
269 for f in fields + ['id']:
274 class cacheable_osv(osv, Cacheable):
279 super(cacheable_osv, self).__init__()
281 def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'):
286 fields = fields or self._columns.keys()
287 ctx = [context.get(x, False) for x in self._relevant]
288 result, tofetch = [], []
290 res = self.get(self._name, id, ctx)
294 result.append(filter_dict(res, fields))
296 # gen the list of "local" (ie not inherited) fields which are classic or many2one
297 nfields = filter(lambda x: x[1]._classic_write, self._columns.items())
298 # gen the list of inherited fields
299 inherits = map(lambda x: (x[0], x[1][2]), self._inherit_fields.items())
300 # complete the field list with the inherited fields which are classic or many2one
301 nfields += filter(lambda x: x[1]._classic_write, inherits)
302 nfields = [x[0] for x in nfields]
304 res = super(cacheable_osv, self).read(cr, user, tofetch, nfields, context, load)
306 self.add((self._name, r['id'], ctx), r)
307 result.append(filter_dict(r, fields))
309 # Appel de fonction si necessaire
315 fvals = self._columns[f].get(cr, self, ids, f, user, context=context)
317 r[f] = fvals[r['id']]
319 # TODO: tri par self._order !!
322 def invalidate(self, key):
323 del self._cache[key[0]][key[1]]
325 def write(self, cr, user, ids, values, context=None):
329 self.invalidate((self._name, id))
330 return super(cacheable_osv, self).write(cr, user, ids, values, context)
332 def unlink(self, cr, user, ids):
334 return super(cacheable_osv, self).unlink(cr, user, ids)
340 #class FakePool(object):
341 # def __init__(self, module):
342 # self.preferred_module = module
344 # def get(self, name):
345 # localpool = module_objects_dict.get(self.preferred_module, {'dict': {}})['dict']
346 # if name in localpool:
347 # obj = localpool[name]
349 # obj = pooler.get_pool(cr.dbname).get(name)
353 # class fake_class(obj.__class__):
354 # def __init__(self):
355 # super(fake_class, self).__init__()
356 # self.pool = fake_pool
358 # return fake_class()