[MERGE] sync with trunk
[odoo/odoo.git] / openerp / osv / fields.py
index 42bc9b7..6fef850 100644 (file)
@@ -45,6 +45,7 @@ import openerp
 import openerp.netsvc as netsvc
 import openerp.tools as tools
 from openerp.tools.translate import _
+import json
 
 def _symbol_set(symb):
     if symb == None or symb == False:
@@ -1180,6 +1181,94 @@ class related(function):
                 result[-1]['relation'] = f['relation']
         self._relations = result
 
+
+class sparse(function):   
+
+    def convert_value(self, obj, cr, uid, record, value, read_value, context=None):        
+        """
+            + For a many2many field, a list of tuples is expected.
+              Here is the list of tuple that are accepted, with the corresponding semantics ::
+
+                 (0, 0,  { values })    link to a new record that needs to be created with the given values dictionary
+                 (1, ID, { values })    update the linked record with id = ID (write *values* on it)
+                 (2, ID)                remove and delete the linked record with id = ID (calls unlink on ID, that will delete the object completely, and the link to it as well)
+                 (3, ID)                cut the link to the linked record with id = ID (delete the relationship between the two objects but does not delete the target object itself)
+                 (4, ID)                link to existing record with id = ID (adds a relationship)
+                 (5)                    unlink all (like using (3,ID) for all linked records)
+                 (6, 0, [IDs])          replace the list of linked IDs (like using (5) then (4,ID) for each ID in the list of IDs)
+
+                 Example:
+                    [(6, 0, [8, 5, 6, 4])] sets the many2many to ids [8, 5, 6, 4]
+
+            + For a one2many field, a lits of tuples is expected.
+              Here is the list of tuple that are accepted, with the corresponding semantics ::
+
+                 (0, 0,  { values })    link to a new record that needs to be created with the given values dictionary
+                 (1, ID, { values })    update the linked record with id = ID (write *values* on it)
+                 (2, ID)                remove and delete the linked record with id = ID (calls unlink on ID, that will delete the object completely, and the link to it as well)
+
+                 Example:
+                    [(0, 0, {'field_name':field_value_record1, ...}), (0, 0, {'field_name':field_value_record2, ...})]
+        """
+
+        if self._type == 'many2many':
+            assert value[0][0] == 6, 'Unsupported m2m value for sparse field: %s' % value
+            return value[0][2]
+
+        elif self._type == 'one2many':
+            if not read_value:
+                read_value = []
+            relation_obj = obj.pool.get(self.relation)
+            for vals in value:
+                assert vals[0] in (0,1,2), 'Unsupported o2m value for sparse field: %s' % vals
+                if vals[0] == 0:
+                    read_value.append(relation_obj.create(cr, uid, vals[2], context=context))
+                elif vals[0] == 1:
+                    relation_obj.write(cr, uid, vals[1], vals[2], context=context)
+                elif vals[0] == 2:
+                    relation_obj.unlink(cr, uid, vals[1], context=context)
+                    read_value.remove(vals[1])
+            return read_value
+        return value
+
+
+    def _fnct_write(self,obj,cr, uid, ids, field_name, value, args, context=None):
+        if not type(ids) == list:
+            ids = [ids]
+        records = obj.browse(cr, uid, ids, context=context)
+        for record in records:
+            # grab serialized value as object - already deserialized
+            serialized = getattr(record, self.serialization_field)
+            if value is None:
+                # simply delete the key to unset it.
+                serialized.pop(field_name, None)
+            else: 
+                serialized[field_name] = self.convert_value(obj, cr, uid, record, value, serialized.get(field_name), context=context)
+            obj.write(cr, uid, ids, {self.serialization_field: serialized}, context=context)
+        return True
+
+    def _fnct_read(self, obj, cr, uid, ids, field_names, args, context=None):
+        results = {}
+        records = obj.browse(cr, uid, ids, context=context)
+        for record in records:
+            # grab serialized value as object - already deserialized
+            serialized = getattr(record, self.serialization_field)
+            results[record.id] = {}
+            for field_name in field_names:
+                if obj._columns[field_name]._type in ['one2many']:
+                    value = serialized.get(field_name, [])
+                else:
+                    results[record.id].update(field_name=value)
+        return results
+
+    def __init__(self, serialization_field, **kwargs):
+        self.serialization_field = serialization_field
+        return super(sparse, self).__init__(self._fnct_read, fnct_inv=self._fnct_write, multi='__sparse_multi', method=True, **kwargs)
+     
+
+
+
+
 # ---------------------------------------------------------
 # Dummy fields
 # ---------------------------------------------------------
@@ -1202,14 +1291,26 @@ class dummy(function):
 # ---------------------------------------------------------
 # Serialized fields
 # ---------------------------------------------------------
+
 class serialized(_column):
-    def __init__(self, string='unknown', serialize_func=repr, deserialize_func=eval, type='text', **args):
-        self._serialize_func = serialize_func
-        self._deserialize_func = deserialize_func
-        self._type = type
-        self._symbol_set = (self._symbol_c, self._serialize_func)
-        self._symbol_get = self._deserialize_func
-        super(serialized, self).__init__(string=string, **args)
+    """ A field able to store an arbitrary python data structure.
+    
+        Note: only plain components allowed.
+    """
+    
+    def _symbol_set_struct(val):
+        return json.dumps(val)
+
+    def _symbol_get_struct(self, val):
+        return json.loads(val or '{}')
+    
+    _prefetch = False
+    _type = 'serialized'
+
+    _symbol_c = '%s'
+    _symbol_f = _symbol_set_struct
+    _symbol_set = (_symbol_c, _symbol_f)
+    _symbol_get = _symbol_get_struct
 
 # TODO: review completly this class for speed improvement
 class property(function):