merge
[openerp-client.git] / bin / widget / model / field.py
blob77dd37b24537f8390d1149ec82cf0bfce6f8e941
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 SelectionField(CharField):
145 def set(self, model, value, test_state=True, modified=False):
146 if value in [sel[0] for sel in self.attrs['selection']]:
147 super(SelectionField, self).set(model, value, test_state, modified)
149 class FloatField(CharField):
150 def validate(self, model):
151 self.get_state_attrs(model)['valid'] = True
152 return True
154 def set_client(self, model, value, test_state=True, force_change=False):
155 internal = model.value[self.name]
156 self.set(model, value, test_state)
157 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]))):
158 if not self.get_state_attrs(model).get('readonly', False):
159 model.modified = True
160 model.modified_fields.setdefault(self.name)
161 self.sig_changed(model)
162 model.signal('record-changed', model)
164 class IntegerField(CharField):
166 def get(self, model, check_load=True, readonly=True, modified=False):
167 return model.value.get(self.name, 0) or 0
169 def get_client(self, model):
170 return model.value[self.name] or 0
172 def validate(self, model):
173 self.get_state_attrs(model)['valid'] = True
174 return True
177 class M2OField(CharField):
179 internal = (id, name)
182 def create(self, model):
183 return False
185 def get(self, model, check_load=True, readonly=True, modified=False):
186 if model.value[self.name]:
187 return model.value[self.name][0] or False
188 return False
190 def get_client(self, model):
191 #model._check_load()
192 if model.value[self.name]:
193 return model.value[self.name][1]
194 return False
196 def set(self, model, value, test_state=False, modified=False):
197 if value and isinstance(value, (int, str, unicode, long)):
198 rpc2 = RPCProxy(self.attrs['relation'])
199 result = rpc2.name_get([value], rpc.session.context)
200 model.value[self.name] = result[0]
201 else:
202 model.value[self.name] = value
203 if modified:
204 model.modified = True
205 model.modified_fields.setdefault(self.name)
207 def set_client(self, model, value, test_state=False, force_change=False):
208 internal = model.value[self.name]
209 self.set(model, value, test_state)
210 if internal != model.value[self.name]:
211 model.modified = True
212 model.modified_fields.setdefault(self.name)
213 self.sig_changed(model)
214 model.signal('record-changed', model)
215 elif force_change:
216 self.sig_changed(model)
218 class M2MField(CharField):
220 internal = [id]
223 def __init__(self, parent, attrs):
224 super(M2MField, self).__init__(parent, attrs)
226 def create(self, model):
227 return []
229 def get(self, model, check_load=True, readonly=True, modified=False):
230 return [(6, 0, model.value[self.name] or [])]
232 def get_client(self, model):
233 return model.value[self.name] or []
235 def set(self, model, value, test_state=False, modified=False):
236 model.value[self.name] = value or []
237 if modified:
238 model.modified = True
239 model.modified_fields.setdefault(self.name)
241 def set_client(self, model, value, test_state=False, force_change=False):
242 internal = model.value[self.name]
243 self.set(model, value, test_state, modified=False)
244 if set(internal) != set(value):
245 model.modified = True
246 model.modified_fields.setdefault(self.name)
247 self.sig_changed(model)
248 model.signal('record-changed', model)
250 def get_default(self, model):
251 return self.get_client(model)
253 # Decorator printing debugging output.
254 def debugger(f):
255 def debugf(*args,**kwargs):
256 print "DEBUG:", f.__name__, args, kwargs
257 retv = f(*args,**kwargs)
258 print "Function returned:", repr(retv)
259 return retv
260 return debugf
263 class debug_function(object):
264 def __init__(self, f):
265 self.__f = f
267 def __call__(self, *args, **kwargs):
268 print 'CALL', args, kwargs
269 self.__numCalls += 1
270 return self.__f(*args, **kwargs)
273 class O2MField(CharField):
275 internal = ModelRecordGroup of the related objects
278 def __init__(self, parent, attrs):
279 super(O2MField, self).__init__(parent, attrs)
280 self.context={}
282 def create(self, model):
283 from widget.model.group import ModelRecordGroup
284 mod = ModelRecordGroup(resource=self.attrs['relation'], fields={}, parent=model)
285 mod.signal_connect(mod, 'model-changed', self._model_changed)
286 return mod
288 def _model_changed(self, group, model):
289 model.parent.modified = True
290 model.parent.modified_fields.setdefault(self.name)
291 self.sig_changed(model.parent)
292 self.parent.signal('record-changed', model)
294 def get_client(self, model):
295 return model.value[self.name]
297 def get(self, model, check_load=True, readonly=True, modified=False):
298 if not model.value[self.name]:
299 return []
300 result = []
301 for model2 in model.value[self.name].models:
302 if (modified and not model2.is_modified()) or \
303 (not model2.id and not model2.is_modified()):
304 continue
305 if model2.id:
306 result.append((1,model2.id, model2.get(check_load=check_load, get_readonly=readonly)))
307 else:
308 result.append((0,0, model2.get(check_load=check_load, get_readonly=readonly)))
309 for id in model.value[self.name].model_removed:
310 result.append((2,id, False))
311 return result
313 def set(self, model, value, test_state=False, modified=False):
314 from widget.model.group import ModelRecordGroup
315 mod = ModelRecordGroup(resource=self.attrs['relation'], fields={}, parent=model)
316 mod.signal_connect(mod, 'model-changed', self._model_changed)
317 model.value[self.name] =mod
318 #self.internal.signal_connect(self.internal, 'model-changed', self._model_changed)
319 model.value[self.name].pre_load(value, display=False)
320 #self.internal.signal_connect(self.internal, 'model-changed', self._model_changed)
322 def set_client(self, model, value, test_state=False, force_change=False):
323 self.set(model, value, test_state=test_state)
324 model.signal('record-changed', model)
326 def set_default(self, model, value):
327 from widget.model.group import ModelRecordGroup
328 fields = {}
329 if value and len(value):
330 context = self.context_get(model)
331 rpc2 = RPCProxy(self.attrs['relation'])
332 fields = rpc2.fields_get(value[0].keys(), context)
334 model.value[self.name] = ModelRecordGroup(resource=self.attrs['relation'], fields=fields, parent=model)
335 model.value[self.name].signal_connect(model.value[self.name], 'model-changed', self._model_changed)
336 mod=None
337 for record in (value or []):
338 mod = model.value[self.name].model_new(default=False)
339 mod.set_default(record)
340 model.value[self.name].model_add(mod)
341 model.value[self.name].current_model = mod
342 #mod.signal('record-changed')
343 return True
345 def get_default(self, model):
346 res = map(lambda x: x.get_default(), model.value[self.name].models or [])
347 return res
349 def validate(self, model):
350 ok = True
351 for model2 in model.value[self.name].models:
352 if not model2.validate():
353 if not model2.is_modified():
354 model.value[self.name].models.remove(model2)
355 else:
356 ok = False
357 if not super(O2MField, self).validate(model):
358 ok = False
359 self.get_state_attrs(model)['valid'] = ok
360 return ok
362 class ReferenceField(CharField):
363 def get_client(self, model):
364 if model.value[self.name]:
365 return model.value[self.name]
366 return False
368 def get(self, model, check_load=True, readonly=True, modified=False):
369 if model.value[self.name]:
370 return '%s,%d' % (model.value[self.name][0], model.value[self.name][1][0])
371 return False
373 def set_client(self, model, value, test_state=False, force_change=False):
374 internal = model.value[self.name]
375 model.value[self.name] = value
376 if (internal or False) != (model.value[self.name] or False):
377 model.modified = True
378 model.modified_fields.setdefault(self.name)
379 self.sig_changed(model)
380 model.signal('record-changed', model)
382 def set(self, model, value, test_state=False, modified=False):
383 if not value:
384 model.value[self.name] = False
385 return
386 ref_model, id = value.split(',')
387 rpc2 = RPCProxy(ref_model)
388 result = rpc2.name_get([id], rpc.session.context)
389 if result:
390 model.value[self.name] = ref_model, result[0]
391 else:
392 model.value[self.name] = False
393 if modified:
394 model.modified = True
395 model.modified_fields.setdefault(self.name)
397 TYPES = {
398 'char' : CharField,
399 'float_time': FloatField,
400 'integer' : IntegerField,
401 'float' : FloatField,
402 'many2one' : M2OField,
403 'many2many' : M2MField,
404 'one2many' : O2MField,
405 'reference' : ReferenceField,
406 'selection': SelectionField,
407 'boolean': IntegerField,
411 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: