Merge
[openerp-client.git] / bin / widget / model / field.py
blobaf8966dde3a55b5eab912cc6f047d21fd7e2f9db
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 if model.is_wizard():
150 get_binary_size = False
151 model.value[self.name] = None
152 name = get_binary_size and self.get_size_name() or self.name
153 model.value[name] = value
154 if model.is_wizard():
155 model.value[self.get_size_name()] = tools.human_size(len(value))
156 if modified:
157 model.modified = True
158 model.modified_fields.setdefault(self.name)
159 return True
161 def get(self, model, check_load=True, readonly=True, modified=False):
162 if self.name in model.value:
163 if (model.value[self.name] is None) and (model.id):
164 c = rpc.session.context.copy()
165 c.update(model.context_get())
166 c['get_binary_size'] = False
167 value = model.rpc.read([model.id], [self.name], c)[0][self.name]
168 self.set(model, value, modified=modified, get_binary_size=False)
169 return model.value.get(self.name, False) or False
171 def get_client(self, model):
172 return model.value.get(self.get_size_name(), False)
174 def set_client(self, model, value, test_state=True, force_change=False):
175 before = self.get(model)
176 self.set(model, value, test_state, get_binary_size=False)
177 if before != self.get(model):
178 model.modified = True
179 model.modified_fields.setdefault(self.name)
180 self.sig_changed(model)
181 model.signal('record-changed', model)
184 class SelectionField(CharField):
185 def set(self, model, value, test_state=True, modified=False):
186 if not self.get_state_attrs(model).get('required', False) and value is None:
187 super(SelectionField, self).set(model, value, test_state, modified)
189 if value in [sel[0] for sel in self.attrs['selection']]:
190 super(SelectionField, self).set(model, value, test_state, modified)
192 class FloatField(CharField):
193 def validate(self, model):
194 self.get_state_attrs(model)['valid'] = True
195 return True
197 def set_client(self, model, value, test_state=True, force_change=False):
198 internal = model.value[self.name]
199 self.set(model, value, test_state)
200 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]))):
201 if not self.get_state_attrs(model).get('readonly', False):
202 model.modified = True
203 model.modified_fields.setdefault(self.name)
204 self.sig_changed(model)
205 model.signal('record-changed', model)
207 class IntegerField(CharField):
209 def get(self, model, check_load=True, readonly=True, modified=False):
210 return model.value.get(self.name, 0) or 0
212 def get_client(self, model):
213 return model.value[self.name] or 0
215 def validate(self, model):
216 self.get_state_attrs(model)['valid'] = True
217 return True
220 class M2OField(CharField):
222 internal = (id, name)
225 def create(self, model):
226 return False
228 def get(self, model, check_load=True, readonly=True, modified=False):
229 if model.value[self.name]:
230 return model.value[self.name][0] or False
231 return False
233 def get_client(self, model):
234 #model._check_load()
235 if model.value[self.name]:
236 return model.value[self.name][1]
237 return False
239 def set(self, model, value, test_state=False, modified=False):
240 if value and isinstance(value, (int, str, unicode, long)):
241 rpc2 = RPCProxy(self.attrs['relation'])
242 result = rpc2.name_get([value], rpc.session.context)
243 model.value[self.name] = result[0]
244 else:
245 model.value[self.name] = value
246 if modified:
247 model.modified = True
248 model.modified_fields.setdefault(self.name)
250 def set_client(self, model, value, test_state=False, force_change=False):
251 internal = model.value[self.name]
252 self.set(model, value, test_state)
253 if internal != model.value[self.name]:
254 model.modified = True
255 model.modified_fields.setdefault(self.name)
256 self.sig_changed(model)
257 model.signal('record-changed', model)
258 elif force_change:
259 self.sig_changed(model)
261 class M2MField(CharField):
263 internal = [id]
266 def __init__(self, parent, attrs):
267 super(M2MField, self).__init__(parent, attrs)
269 def create(self, model):
270 return []
272 def get(self, model, check_load=True, readonly=True, modified=False):
273 return [(6, 0, model.value[self.name] or [])]
275 def get_client(self, model):
276 return model.value[self.name] or []
278 def set(self, model, value, test_state=False, modified=False):
279 model.value[self.name] = value or []
280 if modified:
281 model.modified = True
282 model.modified_fields.setdefault(self.name)
284 def set_client(self, model, value, test_state=False, force_change=False):
285 internal = model.value[self.name]
286 self.set(model, value, test_state, modified=False)
287 if set(internal) != set(value):
288 model.modified = True
289 model.modified_fields.setdefault(self.name)
290 self.sig_changed(model)
291 model.signal('record-changed', model)
293 def get_default(self, model):
294 return self.get_client(model)
296 # Decorator printing debugging output.
297 def debugger(f):
298 def debugf(*args,**kwargs):
299 print "DEBUG:", f.__name__, args, kwargs
300 retv = f(*args,**kwargs)
301 print "Function returned:", repr(retv)
302 return retv
303 return debugf
306 class debug_function(object):
307 def __init__(self, f):
308 self.__f = f
310 def __call__(self, *args, **kwargs):
311 print 'CALL', args, kwargs
312 self.__numCalls += 1
313 return self.__f(*args, **kwargs)
316 class O2MField(CharField):
318 internal = ModelRecordGroup of the related objects
321 def __init__(self, parent, attrs):
322 super(O2MField, self).__init__(parent, attrs)
323 self.context={}
325 def create(self, model):
326 from widget.model.group import ModelRecordGroup
327 mod = ModelRecordGroup(resource=self.attrs['relation'], fields={}, parent=model)
328 mod.signal_connect(mod, 'model-changed', self._model_changed)
329 return mod
331 def _model_changed(self, group, model):
332 model.parent.modified = True
333 model.parent.modified_fields.setdefault(self.name)
334 self.sig_changed(model.parent)
335 self.parent.signal('record-changed', model)
337 def get_client(self, model):
338 return model.value[self.name]
340 def get(self, model, check_load=True, readonly=True, modified=False):
341 if not model.value[self.name]:
342 return []
343 result = []
344 for model2 in model.value[self.name].models:
345 if (modified and not model2.is_modified()) or \
346 (not model2.id and not model2.is_modified()):
347 continue
348 if model2.id:
349 result.append((1,model2.id, model2.get(check_load=check_load, get_readonly=readonly)))
350 else:
351 result.append((0,0, model2.get(check_load=check_load, get_readonly=readonly)))
352 for id in model.value[self.name].model_removed:
353 result.append((2,id, False))
354 return result
356 def set(self, model, value, test_state=False, modified=False):
357 from widget.model.group import ModelRecordGroup
358 mod = ModelRecordGroup(resource=self.attrs['relation'], fields={}, parent=model)
359 mod.signal_connect(mod, 'model-changed', self._model_changed)
360 model.value[self.name] =mod
361 #self.internal.signal_connect(self.internal, 'model-changed', self._model_changed)
362 model.value[self.name].pre_load(value, display=False)
363 #self.internal.signal_connect(self.internal, 'model-changed', self._model_changed)
365 def set_client(self, model, value, test_state=False, force_change=False):
366 self.set(model, value, test_state=test_state)
367 model.signal('record-changed', model)
369 def set_default(self, model, value):
370 from widget.model.group import ModelRecordGroup
371 fields = {}
372 if value and len(value):
373 context = self.context_get(model)
374 rpc2 = RPCProxy(self.attrs['relation'])
375 fields = rpc2.fields_get(value[0].keys(), context)
377 model.value[self.name] = ModelRecordGroup(resource=self.attrs['relation'], fields=fields, parent=model)
378 model.value[self.name].signal_connect(model.value[self.name], 'model-changed', self._model_changed)
379 mod=None
380 for record in (value or []):
381 mod = model.value[self.name].model_new(default=False)
382 mod.set_default(record)
383 model.value[self.name].model_add(mod)
384 model.value[self.name].current_model = mod
385 #mod.signal('record-changed')
386 return True
388 def get_default(self, model):
389 res = map(lambda x: x.get_default(), model.value[self.name].models or [])
390 return res
392 def validate(self, model):
393 ok = True
394 for model2 in model.value[self.name].models:
395 if not model2.validate():
396 if not model2.is_modified():
397 model.value[self.name].models.remove(model2)
398 else:
399 ok = False
400 if not super(O2MField, self).validate(model):
401 ok = False
402 self.get_state_attrs(model)['valid'] = ok
403 return ok
405 class ReferenceField(CharField):
406 def get_client(self, model):
407 if model.value[self.name]:
408 return model.value[self.name]
409 return False
411 def get(self, model, check_load=True, readonly=True, modified=False):
412 if model.value[self.name]:
413 return '%s,%d' % (model.value[self.name][0], model.value[self.name][1][0])
414 return False
416 def set_client(self, model, value, test_state=False, force_change=False):
417 internal = model.value[self.name]
418 model.value[self.name] = value
419 if (internal or False) != (model.value[self.name] or False):
420 model.modified = True
421 model.modified_fields.setdefault(self.name)
422 self.sig_changed(model)
423 model.signal('record-changed', model)
425 def set(self, model, value, test_state=False, modified=False):
426 if not value:
427 model.value[self.name] = False
428 return
429 ref_model, id = value.split(',')
430 rpc2 = RPCProxy(ref_model)
431 result = rpc2.name_get([id], rpc.session.context)
432 if result:
433 model.value[self.name] = ref_model, result[0]
434 else:
435 model.value[self.name] = False
436 if modified:
437 model.modified = True
438 model.modified_fields.setdefault(self.name)
440 TYPES = {
441 'char' : CharField,
442 'float_time': FloatField,
443 'integer' : IntegerField,
444 'float' : FloatField,
445 'many2one' : M2OField,
446 'many2many' : M2MField,
447 'one2many' : O2MField,
448 'reference' : ReferenceField,
449 'selection': SelectionField,
450 'boolean': IntegerField,
451 'image': BinaryField,
452 'binary': BinaryField,
456 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: