~openerp-dev/openobject-server/trunk-bug-712254-ysa

« back to all changes in this revision

Viewing changes to bin/osv/osv.py

  • Committer: pinky
  • Date: 2006-12-07 13:41:40 UTC
  • Revision ID: pinky-3f10ee12cea3c4c75cef44ab04ad33ef47432907
New trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
##############################################################################
 
2
#
 
3
# Copyright (c) 2004-2006 TINY SPRL. (http://tiny.be) All Rights Reserved.
 
4
#                    Fabien Pinckaers <fp@tiny.Be>
 
5
#
 
6
# WARNING: This program as such is intended to be used by professional
 
7
# programmers who take the whole responsability of assessing all potential
 
8
# consequences resulting from its eventual inadequacies and bugs
 
9
# End users who are looking for a ready-to-use solution with commercial
 
10
# garantees and support are strongly adviced to contract a Free Software
 
11
# Service Company
 
12
#
 
13
# This program is Free Software; you can redistribute it and/or
 
14
# modify it under the terms of the GNU General Public License
 
15
# as published by the Free Software Foundation; either version 2
 
16
# of the License, or (at your option) any later version.
 
17
#
 
18
# This program is distributed in the hope that it will be useful,
 
19
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
20
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
21
# GNU General Public License for more details.
 
22
#
 
23
# You should have received a copy of the GNU General Public License
 
24
# along with this program; if not, write to the Free Software
 
25
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
26
#
 
27
##############################################################################
 
28
 
 
29
#
 
30
# OSV: Objects Services
 
31
#
 
32
 
 
33
import orm
 
34
import netsvc
 
35
import pooler
 
36
import copy
 
37
 
 
38
import psycopg
 
39
 
 
40
module_list = []
 
41
module_class_list = {}
 
42
class_pool = {}
 
43
 
 
44
class except_osv(Exception):
 
45
        def __init__(self, name, value, exc_type='warning'):
 
46
                self.name = name
 
47
                self.exc_type = exc_type
 
48
                self.value = value
 
49
                self.args = (exc_type,name)
 
50
 
 
51
class osv_pool(netsvc.Service):
 
52
        obj_pool = {}
 
53
        module_object_list = {}
 
54
 
 
55
        def __init__(self):
 
56
                self.created = []
 
57
                self._sql_error = {}
 
58
                netsvc.Service.__init__(self, 'object_proxy', audience='')
 
59
                self.joinGroup('web-services')
 
60
                self.exportMethod(self.exportedMethods)
 
61
                self.exportMethod(self.obj_list)
 
62
                self.exportMethod(self.exec_workflow)
 
63
                self.exportMethod(self.execute)
 
64
                self.exportMethod(self.execute_cr)
 
65
 
 
66
        def execute_cr(self, cr, uid, obj, method, *args, **kw):
 
67
                #
 
68
                # TODO: check security level
 
69
                #
 
70
                try:
 
71
                        if (not method in getattr(self.obj_pool[obj],'_protected')) and len(args) and args[0] and len(self.obj_pool[obj]._inherits):
 
72
                                types = {obj: args[0]}
 
73
                                cr.execute('select inst_type,inst_id,obj_id from inherit where obj_type=%s and  obj_id in ('+','.join(map(str,args[0]))+')', (obj,))
 
74
                                for ty,id,id2 in cr.fetchall():
 
75
                                        if not ty in types:
 
76
                                                types[ty]=[]
 
77
                                        types[ty].append(id)
 
78
                                        types[obj].remove(id2)
 
79
                                for t,ids in types.items():
 
80
                                        if len(ids):
 
81
                                                t = self.obj_pool[t]
 
82
                                                res = getattr(t,method)(cr, uid, ids, *args[1:], **kw)
 
83
                        else:
 
84
                                obj = self.obj_pool[obj]
 
85
                                res = getattr(obj,method)(cr, uid, *args, **kw)
 
86
                        return res
 
87
                except orm.except_orm, inst:
 
88
                        self.abortResponse(1, inst.value[0], inst.name, inst.value[1])
 
89
                except except_osv, inst:
 
90
                        self.abortResponse(1, inst.name, inst.exc_type, inst.value)
 
91
                except psycopg.IntegrityError, inst:
 
92
                        for key in self._sql_error.keys():
 
93
                                if key in inst[0]:
 
94
                                        self.abortResponse(1, 'Constraint Error', 'warning', self._sql_error[key])
 
95
                        self.abortResponse(1, 'Integrity Error', 'warning', inst[0])
 
96
 
 
97
 
 
98
        def execute(self, db, uid, obj, method, *args, **kw):
 
99
                cr = pooler.get_db(db).cursor()
 
100
                try:
 
101
                        try:
 
102
                                res = self.execute_cr(cr, uid, obj, method, *args, **kw)
 
103
                                cr.commit()
 
104
                        except Exception:
 
105
                                cr.rollback()
 
106
                                raise
 
107
                finally:
 
108
                        cr.close()
 
109
                return res
 
110
 
 
111
        def exec_workflow_cr(self, cr, uid, obj, method, *args):
 
112
                wf_service = netsvc.LocalService("workflow")
 
113
                wf_service.trg_validate(uid, obj, args[0], method, cr)
 
114
                return True
 
115
 
 
116
        def exec_workflow(self, db, uid, obj, method, *args):
 
117
                cr = pooler.get_db(db).cursor()
 
118
                try:
 
119
                        try:
 
120
                                res = self.exec_workflow_cr(cr, uid, obj, method, *args)
 
121
                                cr.commit()
 
122
                        except Exception:
 
123
                                cr.rollback()
 
124
                                raise
 
125
                finally:
 
126
                        cr.close()
 
127
                return res
 
128
 
 
129
        def obj_list(self):
 
130
                return self.obj_pool.keys()
 
131
 
 
132
        # adds a new object instance to the object pool. 
 
133
        # if it already existed, the instance is replaced
 
134
        def add(self, name, obj_inst):
 
135
                if self.obj_pool.has_key(name):
 
136
                        del self.obj_pool[name]
 
137
                self.obj_pool[name] = obj_inst
 
138
 
 
139
                module = str(obj_inst.__class__)[6:]
 
140
                module = module[:len(module)-1]
 
141
                module = module.split('.')[0][2:]
 
142
                self.module_object_list.setdefault(module, []).append(obj_inst)
 
143
 
 
144
        def get(self, name):
 
145
                obj = self.obj_pool.get(name, None)
 
146
# We cannot uncomment this line because it breaks initialisation since objects do not initialize
 
147
# in the correct order and the ORM doesnt support correctly when some objets do not exist yet
 
148
#               assert obj, "object %s does not exist !" % name
 
149
                return obj
 
150
 
 
151
        #TODO: pass a list of modules to load
 
152
        def instanciate(self, module):
 
153
#               print "module list:", module_list
 
154
#               for module in module_list:
 
155
                res = []
 
156
                class_list = module_class_list.get(module, [])
 
157
#                       if module not in self.module_object_list:
 
158
#               print "%s class_list:" % module, class_list
 
159
                for klass in class_list:
 
160
                        res.append(klass.createInstance(self, module))
 
161
                return res
 
162
#                       else:
 
163
#                               print "skipping module", module
 
164
 
 
165
#pooler.get_pool(cr.dbname) = osv_pool()
 
166
 
 
167
#
 
168
# See if we can use the pool var instead of the class_pool one
 
169
#
 
170
class inheritor(type):
 
171
        def __new__(cls, name, bases, d):
 
172
                parent_name = d.get('_inherit', None)
 
173
                if parent_name:
 
174
                        parent_class = class_pool.get(parent_name)
 
175
                        assert parent_class, "parent class %s does not exist !" % parent_name
 
176
                        for s in ('_columns', '_defaults', '_inherits'):
 
177
                                new_dict = copy.copy(getattr(parent_class, s))
 
178
                                new_dict.update(d.get(s, {}))
 
179
                                d[s] = new_dict
 
180
                        bases = (parent_class,)
 
181
                res = type.__new__(cls, name, bases, d)
 
182
                #
 
183
                # update _inherits of others objects
 
184
                #
 
185
                return res
 
186
 
 
187
 
 
188
 
 
189
class osv(orm.orm):
 
190
        #__metaclass__ = inheritor
 
191
 
 
192
        def __new__(cls):
 
193
                if not hasattr(cls, '_module'):
 
194
                        module = str(cls)[6:]
 
195
                        module = module[:len(module)-1]
 
196
                        module = module.split('.')[0][2:]
 
197
                        cls._module = module
 
198
                module_class_list.setdefault(cls._module, []).append(cls)
 
199
                class_pool[cls._name] = cls
 
200
                if module not in module_list:
 
201
                        module_list.append(cls._module)
 
202
                return None
 
203
                
 
204
        #
 
205
        # Goal: try to apply inheritancy at the instanciation level and
 
206
        #       put objects in the pool var
 
207
        #
 
208
        def createInstance(cls, pool, module):
 
209
#               obj = cls()
 
210
                parent_name = hasattr(cls, '_inherit') and cls._inherit
 
211
                if parent_name:
 
212
                        parent_class = pool.get(parent_name).__class__
 
213
                        assert parent_class, "parent class %s does not exist !" % parent_name
 
214
                        ndict = {}
 
215
                        for s in ('_columns', '_defaults', '_inherits'):
 
216
                                new_dict = copy.copy(getattr(pool.get(parent_name), s))
 
217
                                new_dict.update(cls.__dict__.get(s, {}))
 
218
                                ndict[s] = new_dict
 
219
                        #bases = (parent_class,)
 
220
                        #obj.__class__ += (parent_class,)
 
221
                        #res = type.__new__(cls, name, bases, d)
 
222
                        name = hasattr(cls,'_name') and cls._name or cls._inherit
 
223
                        name = str(cls)
 
224
                        cls = type(name, (cls, parent_class), ndict)
 
225
 
 
226
                obj = object.__new__(cls)
 
227
                obj.__init__(pool)
 
228
                return obj
 
229
#               return object.__new__(cls, pool)
 
230
        createInstance = classmethod(createInstance)
 
231
 
 
232
        def __init__(self, pool):
 
233
#               print "__init__", self._name, pool
 
234
                pool.add(self._name, self)
 
235
                self.pool = pool
 
236
                orm.orm.__init__(self)
 
237
                
 
238
#               pooler.get_pool(cr.dbname).add(self._name, self)
 
239
#               print self._name, module
 
240
 
 
241
class Cacheable(object):
 
242
 
 
243
        _cache = {}
 
244
        count = 0
 
245
 
 
246
        def __delete_key(self, key):
 
247
                odico = self._cache
 
248
                for key_item in key[:-1]:
 
249
                        odico = odico[key_item]
 
250
                del odico[key[-1]]
 
251
        
 
252
        def __add_key(self, key, value):
 
253
                odico = self._cache
 
254
                for key_item in key[:-1]:
 
255
                        odico = odico.setdefault(key_item, {})
 
256
                odico[key[-1]] = value
 
257
 
 
258
        def add(self, key, value):
 
259
                self.__add_key(key, value)
 
260
        
 
261
        def invalidate(self, key):
 
262
                self.__delete_key(key)
 
263
        
 
264
        def get(self, key):
 
265
                try:
 
266
                        w = self._cache[key]
 
267
                        return w
 
268
                except KeyError:
 
269
                        return None
 
270
        
 
271
        def clear(self):
 
272
                self._cache.clear()
 
273
                self._items = []
 
274
 
 
275
def filter_dict(d, fields):
 
276
        res = {}
 
277
        for f in fields + ['id']:
 
278
                if f in d:
 
279
                        res[f] = d[f]
 
280
        return res
 
281
 
 
282
class cacheable_osv(osv, Cacheable):
 
283
 
 
284
        _relevant = ['lang']
 
285
 
 
286
        def __init__(self):
 
287
                super(cacheable_osv, self).__init__()
 
288
        
 
289
        def read(self, cr, user, ids, fields=[], context={}, load='_classic_read'):
 
290
                fields = fields or self._columns.keys()
 
291
                ctx = [context.get(x, False) for x in self._relevant]
 
292
                result, tofetch = [], []
 
293
                for id in ids:
 
294
                        res = self.get(self._name, id, ctx)
 
295
                        if not res:
 
296
                                tofetch.append(id)
 
297
                        else:
 
298
                                result.append(filter_dict(res, fields))
 
299
 
 
300
                # gen the list of "local" (ie not inherited) fields which are classic or many2one
 
301
                nfields = filter(lambda x: x[1]._classic_write, self._columns.items())
 
302
                # gen the list of inherited fields
 
303
                inherits = map(lambda x: (x[0], x[1][2]), self._inherit_fields.items())
 
304
                # complete the field list with the inherited fields which are classic or many2one
 
305
                nfields += filter(lambda x: x[1]._classic_write, inherits)
 
306
                nfields = [x[0] for x in nfields]
 
307
 
 
308
                res = super(cacheable_osv, self).read(cr, user, tofetch, nfields, context, load)
 
309
                for r in res:
 
310
                        self.add((self._name, r['id'], ctx), r)
 
311
                        result.append(filter_dict(r, fields))
 
312
 
 
313
                # Appel de fonction si necessaire
 
314
                tofetch = []
 
315
                for f in fields:
 
316
                        if f not in nfields:
 
317
                                tofetch.append(f)
 
318
                for f in tofetch:
 
319
                        fvals = self._columns[f].get(cr, self, ids, f, user, context=context)
 
320
                        for r in result:
 
321
                                r[f] = fvals[r['id']]
 
322
 
 
323
                # TODO: tri par self._order !!
 
324
                return result
 
325
 
 
326
        def invalidate(self, key):
 
327
                del self._cache[key[0]][key[1]]
 
328
        
 
329
        def write(self, cr, user, ids, values, context={}):
 
330
                for id in ids:
 
331
                        self.invalidate((self._name, id))
 
332
                return super(cacheable_osv, self).write(cr, user, ids, values, context)
 
333
        
 
334
        def unlink(self, cr, user, ids):
 
335
                self.clear()
 
336
                return super(cacheable_osv, self).unlink(cr, user, ids)
 
337
 
 
338
#cacheable_osv = osv
 
339
 
 
340
# vim:noexpandtab:
 
341
 
 
342
#class FakePool(object):
 
343
#       def __init__(self, module):
 
344
#               self.preferred_module = module
 
345
        
 
346
#       def get(self, name):
 
347
#               localpool = module_objects_dict.get(self.preferred_module, {'dict': {}})['dict']
 
348
#               if name in localpool:
 
349
#                       obj = localpool[name]
 
350
#               else:
 
351
#                       obj = pooler.get_pool(cr.dbname).get(name)
 
352
#               return obj
 
353
 
 
354
#               fake_pool = self
 
355
#               class fake_class(obj.__class__):
 
356
#                       def __init__(self):
 
357
#                               super(fake_class, self).__init__()
 
358
#                               self.pool = fake_pool
 
359
                                
 
360
#               return fake_class()
 
361