From 482aee1951d562ce655514618354e12f4e4bb17a Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Thu, 22 Dec 2011 19:24:05 +0100 Subject: [PATCH] [IMP] fields.sparse: custom sparse field creation now working + handle deleted resources properly bzr revid: odo@openerp.com-20111222182405-425rzyl6idg9aocu --- openerp/addons/base/ir/ir_model.py | 22 +++++++------ openerp/osv/fields.py | 51 +++++++++++++++++++++--------- 2 files changed, 49 insertions(+), 24 deletions(-) diff --git a/openerp/addons/base/ir/ir_model.py b/openerp/addons/base/ir/ir_model.py index 4531c12766b..88ad218e2c1 100644 --- a/openerp/addons/base/ir/ir_model.py +++ b/openerp/addons/base/ir/ir_model.py @@ -21,6 +21,7 @@ import logging import re import time +import types from osv import fields,osv import netsvc @@ -32,13 +33,12 @@ from tools.translate import _ import pooler def _get_fields_type(self, cr, uid, context=None): - cr.execute('select distinct ttype,ttype from ir_model_fields') - field_types = cr.fetchall() - field_types_copy = field_types - for types in field_types_copy: - if not hasattr(fields,types[0]): - field_types.remove(types) - return field_types + return sorted([(k,k) for k,v in fields.__dict__.iteritems() + if type(v) == types.TypeType + if issubclass(v, fields._column) + if v != fields._column + if not v._deprecated + if not issubclass(v, fields.function)]) def _in_modules(self, cr, uid, ids, field_name, arg, context=None): #pseudo-method used by fields.function in ir.model/ir.model.fields @@ -207,7 +207,11 @@ class ir_model_fields(osv.osv): 'view_load': fields.boolean('View Auto-Load'), 'selectable': fields.boolean('Selectable'), 'modules': fields.function(_in_modules, method=True, type='char', size=128, string='In modules', help='List of modules in which the field is defined'), - 'serialization_field_id': fields.many2one('ir.model.fields', 'Serialization Field', domain = "[('ttype','=','serialized')]", ondelete='cascade'), + 'serialization_field_id': fields.many2one('ir.model.fields', 'Serialization Field', domain = "[('ttype','=','serialized')]", + ondelete='cascade', help="If set, this field will be stored in the sparse " + "structure of the serialization field, instead " + "of having its own database column. This cannot be " + "changed after creation."), } _rec_name='field_description' _defaults = { @@ -300,7 +304,7 @@ class ir_model_fields(osv.osv): if context and context.get('manual',False): vals['state'] = 'manual' - #For the moment renaming a sparse field or changing the storing system is not allowed. This will be done later + #For the moment renaming a sparse field or changing the storing system is not allowed. This may be done later if 'serialization_field_id' in vals or 'name' in vals: for field in self.browse(cr, user, ids, context=context): if 'serialization_field_id' in vals and field.serialization_field_id.id != vals['serialization_field_id']: diff --git a/openerp/osv/fields.py b/openerp/osv/fields.py index 6fef850ada9..53b8d30ccbf 100644 --- a/openerp/osv/fields.py +++ b/openerp/osv/fields.py @@ -45,6 +45,7 @@ import openerp import openerp.netsvc as netsvc import openerp.tools as tools from openerp.tools.translate import _ +from openerp.tools import float_round, float_repr import json def _symbol_set(symb): @@ -74,6 +75,9 @@ class _column(object): _symbol_set = (_symbol_c, _symbol_f) _symbol_get = None + # used to hide a certain field type in the list of field types + _deprecated = False + def __init__(self, string='unknown', required=False, readonly=False, domain=None, context=None, states=None, priority=0, change_default=False, size=None, ondelete=None, translate=False, select=False, manual=False, **args): """ @@ -167,6 +171,7 @@ class integer_big(_column): _symbol_f = lambda x: int(x or 0) _symbol_set = (_symbol_c, _symbol_f) _symbol_get = lambda self,x: x or 0 + _deprecated = True def __init__(self, string='unknown', required=False, **args): super(integer_big, self).__init__(string=string, required=required, **args) @@ -230,17 +235,20 @@ class float(_column): def __init__(self, string='unknown', digits=None, digits_compute=None, required=False, **args): _column.__init__(self, string=string, required=required, **args) self.digits = digits + # synopsis: digits_compute(cr) -> (precision, scale) self.digits_compute = digits_compute if required: warnings.warn("Making a float field `required` has no effect, as NULL values are " "automatically turned into 0.0", PendingDeprecationWarning, stacklevel=2) - def digits_change(self, cr): if self.digits_compute: - t = self.digits_compute(cr) - self._symbol_set=('%s', lambda x: ('%.'+str(t[1])+'f') % (__builtin__.float(x or 0.0),)) - self.digits = t + self.digits = self.digits_compute(cr) + if self.digits: + precision, scale = self.digits + self._symbol_set = ('%s', lambda x: float_repr(float_round(__builtin__.float(x or 0.0), + precision_digits=scale), + precision_digits=scale)) class date(_column): _type = 'date' @@ -270,6 +278,7 @@ class datetime(_column): class time(_column): _type = 'time' + _deprecated = True @staticmethod def now( *args): """ Returns the current time in a format fit for being a @@ -343,6 +352,7 @@ class one2one(_column): _classic_read = False _classic_write = True _type = 'one2one' + _deprecated = True def __init__(self, obj, string='unknown', **args): warnings.warn("The one2one field doesn't work anymore", DeprecationWarning) @@ -991,11 +1001,14 @@ class function(_column): self._symbol_set = integer._symbol_set def digits_change(self, cr): - if self.digits_compute: - t = self.digits_compute(cr) - self._symbol_set=('%s', lambda x: ('%.'+str(t[1])+'f') % (__builtin__.float(x or 0.0),)) - self.digits = t - + if self._type == 'float': + if self.digits_compute: + self.digits = self.digits_compute(cr) + if self.digits: + precision, scale = self.digits + self._symbol_set = ('%s', lambda x: float_repr(float_round(__builtin__.float(x or 0.0), + precision_digits=scale), + precision_digits=scale)) def search(self, cr, uid, obj, name, args, context=None): if not self._fnct_search: @@ -1255,10 +1268,20 @@ class sparse(function): 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) + field_type = obj._columns[field_name]._type + value = serialized.get(field_name, False) + if field_type in ('one2many','many2many'): + value = value or [] + if value: + # filter out deleted records as superuser + relation_obj = obj.pool.get(self.relation) + value = relation_obj.exists(cr, openerp.SUPERUSER_ID, value) + if type(value) in (int,long) and field_type == 'many2one': + relation_obj = obj.pool.get(self.relation) + # check for deleted record as superuser + if not relation_obj.exists(cr, openerp.SUPERUSER_ID, [value]): + value = False + results[record.id][field_name] = value return results def __init__(self, serialization_field, **kwargs): @@ -1267,8 +1290,6 @@ class sparse(function): - - # --------------------------------------------------------- # Dummy fields # ---------------------------------------------------------