diff --git a/doc/03_module_dev_02.rst b/doc/03_module_dev_02.rst index 9c00df6223e..223c82d3f1c 100644 --- a/doc/03_module_dev_02.rst +++ b/doc/03_module_dev_02.rst @@ -843,7 +843,6 @@ A property is a special field: fields.property. relation='product.pricelist', string="Sale Pricelist", method=True, - view_load=True, group_name="Pricelists Properties"), } diff --git a/openerp/addons/base/base.sql b/openerp/addons/base/base.sql index b1ddb2eec70..e91e4516b1a 100644 --- a/openerp/addons/base/base.sql +++ b/openerp/addons/base/base.sql @@ -42,7 +42,6 @@ CREATE TABLE ir_model_fields ( field_description varchar(256), ttype varchar(64), state varchar(64) default 'base', - view_load boolean, relate boolean default False, relation_field varchar(128), translate boolean default False, diff --git a/openerp/addons/base/ir/ir_model.py b/openerp/addons/base/ir/ir_model.py index 165fd8a4be9..7eb4f5ecd94 100644 --- a/openerp/addons/base/ir/ir_model.py +++ b/openerp/addons/base/ir/ir_model.py @@ -245,7 +245,6 @@ class ir_model_fields(osv.osv): "specified as a Python expression defining a list of triplets. " "For example: [('color','=','red')]"), 'groups': fields.many2many('res.groups', 'ir_model_fields_group_rel', 'field_id', 'group_id', 'Groups'), - 'view_load': fields.boolean('View Auto-Load'), 'selectable': fields.boolean('Selectable'), 'modules': fields.function(_in_modules, 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')]", @@ -256,7 +255,6 @@ class ir_model_fields(osv.osv): } _rec_name='field_description' _defaults = { - 'view_load': 0, 'selection': "", 'domain': "[]", 'name': 'x_', @@ -386,7 +384,6 @@ class ir_model_fields(osv.osv): ('size', 'size', int), ('on_delete', 'ondelete', str), ('translate', 'translate', bool), - ('view_load', 'view_load', bool), ('selectable', 'selectable', bool), ('select_level', 'select', int), ('selection', 'selection', eval), diff --git a/openerp/addons/base/res/ir_property.py b/openerp/addons/base/res/ir_property.py index 5ec4f98c3b8..2a751361ddc 100644 --- a/openerp/addons/base/res/ir_property.py +++ b/openerp/addons/base/res/ir_property.py @@ -22,7 +22,7 @@ import time from openerp.osv import osv, fields -from openerp.osv.orm import browse_record +from openerp.osv.orm import browse_record, browse_null from openerp.tools.misc import attrgetter # ------------------------------------------------------------------------- @@ -32,27 +32,10 @@ from openerp.tools.misc import attrgetter class ir_property(osv.osv): _name = 'ir.property' - def _models_field_get(self, cr, uid, field_key, field_value, context=None): - get = attrgetter(field_key, field_value) - obj = self.pool.get('ir.model.fields') - ids = obj.search(cr, uid, [('view_load','=',1)], context=context) - res = set() - for o in obj.browse(cr, uid, ids, context=context): - res.add(get(o)) - return list(res) - - def _models_get(self, cr, uid, context=None): - return self._models_field_get(cr, uid, 'model', 'model_id.name', context) - - def _models_get2(self, cr, uid, context=None): - return self._models_field_get(cr, uid, 'relation', 'relation', context) - - _columns = { 'name': fields.char('Name', size=128, select=1), - 'res_id': fields.reference('Resource', selection=_models_get, size=128, - help="If not set, acts as a default value for new resources", select=1), + 'res_id': fields.char('Resource', size=128, help="If not set, acts as a default value for new resources", select=1), 'company_id': fields.many2one('res.company', 'Company', select=1), 'fields_id': fields.many2one('ir.model.fields', 'Field', ondelete='cascade', required=True, select=1), @@ -60,7 +43,7 @@ class ir_property(osv.osv): 'value_integer' : fields.integer('Value'), 'value_text' : fields.text('Value'), # will contain (char, text) 'value_binary' : fields.binary('Value'), - 'value_reference': fields.reference('Value', selection=_models_get2, size=128), + 'value_reference': fields.char('Value', size=128), 'value_datetime' : fields.datetime('Value'), 'type' : fields.selection([('char', 'Char'), @@ -72,6 +55,7 @@ class ir_property(osv.osv): ('many2one', 'Many2One'), ('date', 'Date'), ('datetime', 'DateTime'), + ('selection', 'Selection'), ], 'Type', required=True, @@ -106,6 +90,7 @@ class ir_property(osv.osv): 'many2one': 'value_reference', 'date' : 'value_datetime', 'datetime' : 'value_datetime', + 'selection': 'value_text', } field = type2field.get(type_) if not field: @@ -128,7 +113,6 @@ class ir_property(osv.osv): values[field] = value return values - def write(self, cr, uid, ids, values, context=None): return super(ir_property, self).write(cr, uid, ids, self._update_values(cr, uid, ids, values), context=context) @@ -136,7 +120,7 @@ class ir_property(osv.osv): return super(ir_property, self).create(cr, uid, self._update_values(cr, uid, None, values), context=context) def get_by_record(self, cr, uid, record, context=None): - if record.type in ('char', 'text'): + if record.type in ('char', 'text', 'selection'): return record.value_text elif record.type == 'float': return record.value_float @@ -147,7 +131,10 @@ class ir_property(osv.osv): elif record.type == 'binary': return record.value_binary elif record.type == 'many2one': - return record.value_reference + if not record.value_reference: + return browse_null() + model, resource_id = record.value_reference.split(',') + return self.pool.get(model).browse(cr, uid, int(resource_id), context=context) elif record.type == 'datetime': return record.value_datetime elif record.type == 'date': @@ -160,7 +147,8 @@ class ir_property(osv.osv): domain = self._get_domain(cr, uid, name, model, context=context) if domain is not None: domain = [('res_id', '=', res_id)] + domain - nid = self.search(cr, uid, domain, context=context) + #make the search with company_id asc to make sure that properties specific to a company are given first + nid = self.search(cr, uid, domain, limit=1, order='company_id asc', context=context) if not nid: return False record = self.browse(cr, uid, nid[0], context=context) return self.get_by_record(cr, uid, record, context=context) diff --git a/openerp/osv/fields.py b/openerp/osv/fields.py index f38d1efa0cc..568a52f2bf4 100644 --- a/openerp/osv/fields.py +++ b/openerp/osv/fields.py @@ -109,7 +109,6 @@ class _column(object): self._context = context self.write = False self.read = False - self.view_load = 0 self.select = select self.manual = manual self.selectable = True @@ -1383,8 +1382,10 @@ class property(function): def _get_by_id(self, obj, cr, uid, prop_name, ids, context=None): prop = obj.pool.get('ir.property') vids = [obj._name + ',' + str(oid) for oid in ids] - - domain = [('fields_id.model', '=', obj._name), ('fields_id.name', 'in', prop_name)] + def_id = self._field_get(cr, uid, obj._name, prop_name[0]) + company = obj.pool.get('res.company') + cid = company._company_default_get(cr, uid, obj._name, def_id, context=context) + domain = [('fields_id.model', '=', obj._name), ('fields_id.name', 'in', prop_name), ('company_id', '=', cid)] #domain = prop._get_domain(cr, uid, prop_name, obj._name, context) if vids: domain = [('res_id', 'in', vids)] + domain @@ -1477,11 +1478,13 @@ class property(function): self.field_id[cr.dbname] = res and res[0] return self.field_id[cr.dbname] - def __init__(self, obj_prop, **args): - # TODO remove obj_prop parameter (use many2one type) + + def __init__(self, **args): self.field_id = {} - function.__init__(self, self._fnct_read, False, self._fnct_write, - obj_prop, multi='properties', **args) + if 'view_load' in args: + _logger.warning("view_load attribute is deprecated on ir.fields. Args: %r", args) + obj = 'relation' in args and args['relation'] or '' + function.__init__(self, self._fnct_read, False, self._fnct_write, obj=obj, multi='properties', **args) def restart(self): self.field_id = {} diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index f1c421af5c9..eb5f637622b 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -773,7 +773,6 @@ class BaseModel(object): 'field_description': f.string, 'ttype': f._type, 'relation': f._obj or '', - 'view_load': (f.view_load and 1) or 0, 'select_level': tools.ustr(f.select or 0), 'readonly': (f.readonly and 1) or 0, 'required': (f.required and 1) or 0, @@ -803,12 +802,12 @@ class BaseModel(object): vals['id'] = id cr.execute("""INSERT INTO ir_model_fields ( id, model_id, model, name, field_description, ttype, - relation,view_load,state,select_level,relation_field, translate, serialization_field_id + relation,state,select_level,relation_field, translate, serialization_field_id ) VALUES ( - %s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s + %s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s )""", ( id, vals['model_id'], vals['model'], vals['name'], vals['field_description'], vals['ttype'], - vals['relation'], bool(vals['view_load']), 'base', + vals['relation'], 'base', vals['select_level'], vals['relation_field'], bool(vals['translate']), vals['serialization_field_id'] )) if 'module' in context: @@ -826,11 +825,11 @@ class BaseModel(object): cr.commit() cr.execute("""UPDATE ir_model_fields SET model_id=%s, field_description=%s, ttype=%s, relation=%s, - view_load=%s, select_level=%s, readonly=%s ,required=%s, selectable=%s, relation_field=%s, translate=%s, serialization_field_id=%s + select_level=%s, readonly=%s ,required=%s, selectable=%s, relation_field=%s, translate=%s, serialization_field_id=%s WHERE model=%s AND name=%s""", ( vals['model_id'], vals['field_description'], vals['ttype'], - vals['relation'], bool(vals['view_load']), + vals['relation'], vals['select_level'], bool(vals['readonly']), bool(vals['required']), bool(vals['selectable']), vals['relation_field'], bool(vals['translate']), vals['serialization_field_id'], vals['model'], vals['name'] )) break @@ -5288,6 +5287,34 @@ class BaseModel(object): # for backward compatibility resolve_o2m_commands_to_record_dicts = resolve_2many_commands + def search_read(self, cr, uid, domain=None, fields=None, offset=0, limit=None, order=None, context=None): + """ + Performs a ``search()`` followed by a ``read()``. + + :param cr: database cursor + :param user: current user id + :param domain: Search domain, see ``args`` parameter in ``search()``. Defaults to an empty domain that will match all records. + :param fields: List of fields to read, see ``fields`` parameter in ``read()``. Defaults to all fields. + :param offset: Number of records to skip, see ``offset`` parameter in ``search()``. Defaults to 0. + :param limit: Maximum number of records to return, see ``limit`` parameter in ``search()``. Defaults to no limit. + :param order: Columns to sort result, see ``order`` parameter in ``search()``. Defaults to no sort. + :param context: context arguments. + :return: List of dictionaries containing the asked fields. + :rtype: List of dictionaries. + + """ + record_ids = self.search(cr, uid, domain or [], offset, limit or False, order or False, context or {}) + if not record_ids: + return [] + result = self.read(cr, uid, record_ids, fields or [], context or {}) + # reorder read + if len(result) >= 1: + index = {} + for r in result: + index[r['id']] = r + result = [index[x] for x in record_ids if x in index] + return result + def _register_hook(self, cr): """ stuff to do right after the registry is built """ pass diff --git a/openerp/tests/test_orm.py b/openerp/tests/test_orm.py index d2ec96846df..91b2cb0f278 100644 --- a/openerp/tests/test_orm.py +++ b/openerp/tests/test_orm.py @@ -76,6 +76,30 @@ class TestORM(common.TransactionCase): with self.assertRaises(Exception): self.partner.unlink(cr, uid2, [p1,p2]) + @mute_logger('openerp.osv.orm') + def test_search_read(self): + # simple search_read + self.partner.create(self.cr, UID, {'name': 'MyPartner1'}) + found = self.partner.search_read(self.cr, UID, [['name', '=', 'MyPartner1']], ['name']) + self.assertEqual(len(found), 1) + self.assertEqual(found[0]['name'], 'MyPartner1') + self.assertTrue('id' in found[0]) + + # search_read correct order + self.partner.create(self.cr, UID, {'name': 'MyPartner2'}) + found = self.partner.search_read(self.cr, UID, [['name', 'like', 'MyPartner']], ['name'], order="name") + self.assertEqual(len(found), 2) + self.assertEqual(found[0]['name'], 'MyPartner1') + self.assertEqual(found[1]['name'], 'MyPartner2') + found = self.partner.search_read(self.cr, UID, [['name', 'like', 'MyPartner']], ['name'], order="name desc") + self.assertEqual(len(found), 2) + self.assertEqual(found[0]['name'], 'MyPartner2') + self.assertEqual(found[1]['name'], 'MyPartner1') + + # search_read that finds nothing + found = self.partner.search_read(self.cr, UID, [['name', '=', 'Does not exists']], ['name']) + self.assertEqual(len(found), 0) + class TestInherits(common.TransactionCase): """ test the behavior of the orm for models that use _inherits;