work on binary files:
[openerp-client.git] / bin / widget / model / field.py
blob2e3172bc5566de1c5310ffdae4d9931183f4a4bc
1 # -*- encoding: utf-8 -*-
2 ##############################################################################
4 # Copyright (c) 2004-2008 TINY SPRL. (http://tiny.be) All Rights Reserved.
6 # $Id$
8 # WARNING: This program as such is intended to be used by professional
9 # programmers who take the whole responsability of assessing all potential
10 # consequences resulting from its eventual inadequacies and bugs
11 # End users who are looking for a ready-to-use solution with commercial
12 # garantees and support are strongly adviced to contract a Free Software
13 # Service Company
15 # This program is Free Software; you can redistribute it and/or
16 # modify it under the terms of the GNU General Public License
17 # as published by the Free Software Foundation; either version 2
18 # of the License, or (at your option) any later version.
20 # This program is distributed in the hope that it will be useful,
21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 # GNU General Public License for more details.
25 # You should have received a copy of the GNU General Public License
26 # along with this program; if not, write to the Free Software
27 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 ##############################################################################
31 from rpc import RPCProxy
32 import rpc
33 try:
34 from sets import Set as set
35 except ImportError:
36 pass
37 import tools
38 class ModelField(object):
39 '''
40 get: return the values to write to the server
41 get_client: return the value for the client widget (form_gtk)
42 set: save the value from the server
43 set_client: save the value from the widget
44 '''
46 def __new__(cls, type):
47 klass = TYPES.get(type, CharField)
48 return klass
51 class CharField(object):
52 def __init__(self, parent, attrs):
53 self.parent = parent
54 self.attrs = attrs
55 self.name = attrs['name']
56 self.internal = False
57 self.default_attrs = {}
59 def sig_changed(self, model):
60 if self.get_state_attrs(model).get('readonly', False):
61 return
62 if self.attrs.get('on_change',False):
63 model.on_change(self.attrs['on_change'])
64 if self.attrs.get('change_default', False):
65 model.cond_default(self.attrs['name'], self.get(model))
67 def domain_get(self, model):
68 dom = self.attrs.get('domain', '[]')
69 return model.expr_eval(dom)
71 def context_get(self, model, check_load=True, eval=True):
72 context = {}
73 context.update(self.parent.context)
74 field_context_str = self.attrs.get('context', '{}') or '{}'
75 if eval:
76 field_context = model.expr_eval('dict(%s)' % field_context_str, check_load=check_load)
77 context.update(field_context)
78 return context
80 def validate(self, model):
81 ok = True
82 if bool(int(self.get_state_attrs(model).get('required', 0))):
83 if not model.value[self.name]:
84 ok=False
85 self.get_state_attrs(model)['valid'] = ok
86 return ok
88 def set(self, model, value, test_state=True, modified=False):
89 model.value[self.name] = value
90 if modified:
91 model.modified = True
92 model.modified_fields.setdefault(self.name)
93 return True
95 def get(self, model, check_load=True, readonly=True, modified=False):
96 return model.value.get(self.name, False) or False
98 def set_client(self, model, value, test_state=True, force_change=False):
99 internal = model.value.get(self.name, False)
100 self.set(model, value, test_state)
101 if (internal or False) != (model.value.get(self.name,False) or False):
102 model.modified = True
103 model.modified_fields.setdefault(self.name)
104 self.sig_changed(model)
105 model.signal('record-changed', model)
107 def get_client(self, model):
108 return model.value[self.name] or False
110 def set_default(self, model, value):
111 res = self.set(model, value)
112 if self.attrs.get('on_change',False):
113 model.on_change(self.attrs['on_change'])
114 return res
116 def get_default(self, model):
117 return self.get(model)
119 def create(self, model):
120 return False
122 def attrs_set(self, model):
123 attrs_changes = eval(self.attrs.get('attrs',"{}"))
124 for k,v in attrs_changes.items():
125 for condition in v:
126 result = tools.calc_condition(self,model,condition)
127 if result:
128 self.get_state_attrs(model)[k]=True
129 def state_set(self, model, state='draft'):
130 state_changes = dict(self.attrs.get('states',{}).get(state,[]))
131 for key in ('readonly', 'required'):
132 if key in state_changes:
133 self.get_state_attrs(model)[key] = state_changes[key]
134 else:
135 self.get_state_attrs(model)[key] = self.attrs[key]
136 if 'value' in state_changes:
137 self.set(model, value, test_state=False, modified=True)
139 def get_state_attrs(self, model):
140 if self.name not in model.state_attrs:
141 model.state_attrs[self.name] = self.attrs.copy()
142 return model.state_attrs[self.name]
144 class BinaryField(CharField):
145 def get_size_name(self):
146 return "%s.size" % self.name
148 def set(self, model, value, test_state=True, modified=False, get_binary_size=True):
149 model.value[self.name] = None
150 name = get_binary_size and self.get_size_name() or self.name
151 model.value[name] = value
152 if modified:
153 model.modified = True
154 model.modified_fields.setdefault(self.name)
155 return True
157 def get(self, model, check_load=True, readonly=True, modified=False):
158 if self.name in model.value:
159 if (model.value[self.name] is None) and (model.id):
160 c = rpc.session.context.copy()
161 c.update(model.context_get())
162 c['get_binary_size'] = False
163 value = model.rpc.read([model.id], [self.name], c)[0][self.name]
164 self.set(model, value, modified=modified, get_binary_size=False)
165 return model.value.get(self.name, False)
167 def get_client(self, model):
168 return model.value.get(self.get_size_name(), False)
170 class SelectionField(CharField):
171 def set(self, model, value, test_state=True, modified=False):
172 if not self.get_state_attrs(model).get('required', False) and value is None:
173 super(SelectionField, self).set(model, value, test_state, modified)
175 if value in [sel[0] for sel in self.attrs['selection']]:
176 super(SelectionField, self).set(model, value, test_state, modified)
178 class FloatField(CharField):
179 def validate(self, model):
180 self.get_state_attrs(model)['valid'] = True
181 return True
183 def set_client(self, model, value, test_state=True, force_change=False):
184 internal = model.value[self.name]
185 self.set(model, value, test_state)
186 if abs(float(internal or 0.0) - float(model.value[self.name] or 0.0)) >= (10.0**(-int(self.attrs.get('digits', (12,4))[1]))):
187 if not self.get_state_attrs(model).get('readonly', False):
188 model.modified = True
189 model.modified_fields.setdefault(self.name)
190 self.sig_changed(model)
191 model.signal('record-changed', model)
193 class IntegerField(CharField):
195 def get(self, model, check_load=True, readonly=True, modified=False):
196 return model.value.get(self.name, 0) or 0
198 def get_client(self, model):
199 return model.value[self.name] or 0
201 def validate(self, model):
202 self.get_state_attrs(model)['valid'] = True
203 return True
206 class M2OField(CharField):
208 internal = (id, name)
211 def create(self, model):
212 return False
214 def get(self, model, check_load=True, readonly=True, modified=False):
215 if model.value[self.name]:
216 return model.value[self.name][0] or False
217 return False
219 def get_client(self, model):
220 #model._check_load()
221 if model.value[self.name]:
222 return model.value[self.name][1]
223 return False
225 def set(self, model, value, test_state=False, modified=False):
226 if value and isinstance(value, (int, str, unicode, long)):
227 rpc2 = RPCProxy(self.attrs['relation'])
228 result = rpc2.name_get([value], rpc.session.context)
229 model.value[self.name] = result[0]
230 else:
231 model.value[self.name] = value
232 if modified:
233 model.modified = True
234 model.modified_fields.setdefault(self.name)
236 def set_client(self, model, value, test_state=False, force_change=False):
237 internal = model.value[self.name]
238 self.set(model, value, test_state)
239 if internal != model.value[self.name]:
240 model.modified = True
241 model.modified_fields.setdefault(self.name)
242 self.sig_changed(model)
243 model.signal('record-changed', model)
244 elif force_change:
245 self.sig_changed(model)
247 class M2MField(CharField):
249 internal = [id]
252 def __init__(self, parent, attrs):
253 super(M2MField, self).__init__(parent, attrs)
255 def create(self, model):
256 return []
258 def get(self, model, check_load=True, readonly=True, modified=False):
259 return [(6, 0, model.value[self.name] or [])]
261 def get_client(self, model):
262 return model.value[self.name] or []
264 def set(self, model, value, test_state=False, modified=False):
265 model.value[self.name] = value or []
266 if modified:
267 model.modified = True
268 model.modified_fields.setdefault(self.name)
270 def set_client(self, model, value, test_state=False, force_change=False):
271 internal = model.value[self.name]
272 self.set(model, value, test_state, modified=False)
273 if set(internal) != set(value):
274 model.modified = True
275 model.modified_fields.setdefault(self.name)
276 self.sig_changed(model)
277 model.signal('record-changed', model)
279 def get_default(self, model):
280 return self.get_client(model)
282 # Decorator printing debugging output.
283 def debugger(f):
284 def debugf(*args,**kwargs):
285 print "DEBUG:", f.__name__, args, kwargs
286 retv = f(*args,**kwargs)
287 print "Function returned:", repr(retv)
288 return retv
289 return debugf
292 class debug_function(object):
293 def __init__(self, f):
294 self.__f = f
296 def __call__(self, *args, **kwargs):
297 print 'CALL', args, kwargs
298 self.__numCalls += 1
299 return self.__f(*args, **kwargs)
302 class O2MField(CharField):
304 internal = ModelRecordGroup of the related objects
307 def __init__(self, parent, attrs):
308 super(O2MField, self).__init__(parent, attrs)
309 self.context={}
311 def create(self, model):
312 from widget.model.group import ModelRecordGroup
313 mod = ModelRecordGroup(resource=self.attrs['relation'], fields={}, parent=model)
314 mod.signal_connect(mod, 'model-changed', self._model_changed)
315 return mod
317 def _model_changed(self, group, model):
318 model.parent.modified = True
319 model.parent.modified_fields.setdefault(self.name)
320 self.sig_changed(model.parent)
321 self.parent.signal('record-changed', model)
323 def get_client(self, model):
324 return model.value[self.name]
326 def get(self, model, check_load=True, readonly=True, modified=False):
327 if not model.value[self.name]:
328 return []
329 result = []
330 for model2 in model.value[self.name].models:
331 if (modified and not model2.is_modified()) or \
332 (not model2.id and not model2.is_modified()):
333 continue
334 if model2.id:
335 result.append((1,model2.id, model2.get(check_load=check_load, get_readonly=readonly)))
336 else:
337 result.append((0,0, model2.get(check_load=check_load, get_readonly=readonly)))
338 for id in model.value[self.name].model_removed:
339 result.append((2,id, False))
340 return result
342 def set(self, model, value, test_state=False, modified=False):
343 from widget.model.group import ModelRecordGroup
344 mod = ModelRecordGroup(resource=self.attrs['relation'], fields={}, parent=model)
345 mod.signal_connect(mod, 'model-changed', self._model_changed)
346 model.value[self.name] =mod
347 #self.internal.signal_connect(self.internal, 'model-changed', self._model_changed)
348 model.value[self.name].pre_load(value, display=False)
349 #self.internal.signal_connect(self.internal, 'model-changed', self._model_changed)
351 def set_client(self, model, value, test_state=False, force_change=False):
352 self.set(model, value, test_state=test_state)
353 model.signal('record-changed', model)
355 def set_default(self, model, value):
356 from widget.model.group import ModelRecordGroup
357 fields = {}
358 if value and len(value):
359 context = self.context_get(model)
360 rpc2 = RPCProxy(self.attrs['relation'])
361 fields = rpc2.fields_get(value[0].keys(), context)
363 model.value[self.name] = ModelRecordGroup(resource=self.attrs['relation'], fields=fields, parent=model)
364 model.value[self.name].signal_connect(model.value[self.name], 'model-changed', self._model_changed)
365 mod=None
366 for record in (value or []):
367 mod = model.value[self.name].model_new(default=False)
368 mod.set_default(record)
369 model.value[self.name].model_add(mod)
370 model.value[self.name].current_model = mod
371 #mod.signal('record-changed')
372 return True
374 def get_default(self, model):
375 res = map(lambda x: x.get_default(), model.value[self.name].models or [])
376 return res
378 def validate(self, model):
379 ok = True
380 for model2 in model.value[self.name].models:
381 if not model2.validate():
382 if not model2.is_modified():
383 model.value[self.name].models.remove(model2)
384 else:
385 ok = False
386 if not super(O2MField, self).validate(model):
387 ok = False
388 self.get_state_attrs(model)['valid'] = ok
389 return ok
391 class ReferenceField(CharField):
392 def get_client(self, model):
393 if model.value[self.name]:
394 return model.value[self.name]
395 return False
397 def get(self, model, check_load=True, readonly=True, modified=False):
398 if model.value[self.name]:
399 return '%s,%d' % (model.value[self.name][0], model.value[self.name][1][0])
400 return False
402 def set_client(self, model, value, test_state=False, force_change=False):
403 internal = model.value[self.name]
404 model.value[self.name] = value
405 if (internal or False) != (model.value[self.name] or False):
406 model.modified = True
407 model.modified_fields.setdefault(self.name)
408 self.sig_changed(model)
409 model.signal('record-changed', model)
411 def set(self, model, value, test_state=False, modified=False):
412 if not value:
413 model.value[self.name] = False
414 return
415 ref_model, id = value.split(',')
416 rpc2 = RPCProxy(ref_model)
417 result = rpc2.name_get([id], rpc.session.context)
418 if result:
419 model.value[self.name] = ref_model, result[0]
420 else:
421 model.value[self.name] = False
422 if modified:
423 model.modified = True
424 model.modified_fields.setdefault(self.name)
426 TYPES = {
427 'char' : CharField,
428 'float_time': FloatField,
429 'integer' : IntegerField,
430 'float' : FloatField,
431 'many2one' : M2OField,
432 'many2many' : M2MField,
433 'one2many' : O2MField,
434 'reference' : ReferenceField,
435 'selection': SelectionField,
436 'boolean': IntegerField,
437 'image': BinaryField,
438 'binary': BinaryField,
442 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: