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 model
.value
[self
.name
] = None
150 name
= get_binary_size
and self
.get_size_name() or self
.name
151 model
.value
[name
] = value
153 model
.modified
= True
154 model
.modified_fields
.setdefault(self
.name
)
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
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
206 class M2OField(CharField
):
208 internal = (id, name)
211 def create(self
, model
):
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
219 def get_client(self
, model
):
221 if model
.value
[self
.name
]:
222 return model
.value
[self
.name
][1]
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]
231 model
.value
[self
.name
] = value
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
)
245 self
.sig_changed(model
)
247 class M2MField(CharField
):
252 def __init__(self
, parent
, attrs
):
253 super(M2MField
, self
).__init
__(parent
, attrs
)
255 def create(self
, model
):
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 []
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.
284 def debugf(*args
,**kwargs
):
285 print "DEBUG:", f
.__name
__, args
, kwargs
286 retv
= f(*args
,**kwargs
)
287 print "Function returned:", repr(retv
)
292 class debug_function(object):
293 def __init__(self
, f
):
296 def __call__(self
, *args
, **kwargs
):
297 print 'CALL', args
, kwargs
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
)
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
)
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
]:
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()):
335 result
.append((1,model2
.id, model2
.get(check_load
=check_load
, get_readonly
=readonly
)))
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))
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
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
)
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')
374 def get_default(self
, model
):
375 res
= map(lambda x
: x
.get_default(), model
.value
[self
.name
].models
or [])
378 def validate(self
, model
):
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
)
386 if not super(O2MField
, self
).validate(model
):
388 self
.get_state_attrs(model
)['valid'] = ok
391 class ReferenceField(CharField
):
392 def get_client(self
, model
):
393 if model
.value
[self
.name
]:
394 return model
.value
[self
.name
]
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])
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):
413 model
.value
[self
.name
] = False
415 ref_model
, id = value
.split(',')
416 rpc2
= RPCProxy(ref_model
)
417 result
= rpc2
.name_get([id], rpc
.session
.context
)
419 model
.value
[self
.name
] = ref_model
, result
[0]
421 model
.value
[self
.name
] = False
423 model
.modified
= True
424 model
.modified_fields
.setdefault(self
.name
)
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: