1 # -*- encoding: utf-8 -*-
2 ##############################################################################
4 # Copyright (c) 2004-2008 TINY SPRL. (http://tiny.be) All Rights Reserved.
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
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
34 from sets
import Set
as set
38 class ModelField(object):
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
46 def __new__(cls
, type):
47 klass
= TYPES
.get(type, CharField
)
51 class CharField(object):
52 def __init__(self
, parent
, attrs
):
55 self
.name
= attrs
['name']
57 self
.default_attrs
= {}
59 def sig_changed(self
, model
):
60 if self
.get_state_attrs(model
).get('readonly', False):
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):
73 context
.update(self
.parent
.context
)
74 field_context_str
= self
.attrs
.get('context', '{}') or '{}'
76 field_context
= model
.expr_eval('dict(%s)' % field_context_str
, check_load
=check_load
)
77 context
.update(field_context
)
80 def validate(self
, model
):
82 if bool(int(self
.get_state_attrs(model
).get('required', 0))):
83 if not model
.value
[self
.name
]:
85 self
.get_state_attrs(model
)['valid'] = ok
88 def set(self
, model
, value
, test_state
=True, modified
=False):
89 model
.value
[self
.name
] = value
92 model
.modified_fields
.setdefault(self
.name
)
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'])
116 def get_default(self
, model
):
117 return self
.get(model
)
119 def create(self
, model
):
122 def attrs_set(self
, model
):
123 attrs_changes
= eval(self
.attrs
.get('attrs',"{}"))
124 for k
,v
in attrs_changes
.items():
126 result
= tools
.calc_condition(self
,model
,condition
)
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
]
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
))
157 model
.modified
= True
158 model
.modified_fields
.setdefault(self
.name
)
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
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
220 class M2OField(CharField
):
222 internal = (id, name)
225 def create(self
, model
):
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
233 def get_client(self
, model
):
235 if model
.value
[self
.name
]:
236 return model
.value
[self
.name
][1]
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]
245 model
.value
[self
.name
] = value
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
)
259 self
.sig_changed(model
)
261 class M2MField(CharField
):
266 def __init__(self
, parent
, attrs
):
267 super(M2MField
, self
).__init
__(parent
, attrs
)
269 def create(self
, model
):
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 []
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.
298 def debugf(*args
,**kwargs
):
299 print "DEBUG:", f
.__name
__, args
, kwargs
300 retv
= f(*args
,**kwargs
)
301 print "Function returned:", repr(retv
)
306 class debug_function(object):
307 def __init__(self
, f
):
310 def __call__(self
, *args
, **kwargs
):
311 print 'CALL', args
, kwargs
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
)
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
)
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
]:
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()):
349 result
.append((1,model2
.id, model2
.get(check_load
=check_load
, get_readonly
=readonly
)))
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))
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
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
)
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')
388 def get_default(self
, model
):
389 res
= map(lambda x
: x
.get_default(), model
.value
[self
.name
].models
or [])
392 def validate(self
, model
):
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
)
400 if not super(O2MField
, self
).validate(model
):
402 self
.get_state_attrs(model
)['valid'] = ok
405 class ReferenceField(CharField
):
406 def get_client(self
, model
):
407 if model
.value
[self
.name
]:
408 return model
.value
[self
.name
]
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])
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):
427 model
.value
[self
.name
] = False
429 ref_model
, id = value
.split(',')
430 rpc2
= RPCProxy(ref_model
)
431 result
= rpc2
.name_get([id], rpc
.session
.context
)
433 model
.value
[self
.name
] = ref_model
, result
[0]
435 model
.value
[self
.name
] = False
437 model
.modified
= True
438 model
.modified_fields
.setdefault(self
.name
)
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: