From 66f0ada474524bfd47834bff911c7a729870933c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Tue, 17 Feb 2009 11:40:31 +0100 Subject: [PATCH] Allow to use related fields (many2one and reference) in read Use rec_name function field instead of name_get and name_search Remove get function on many2one field Don't put record name in reference field Handle rec_name in default_get so default value for many2one are now only id --- CHANGELOG | 2 + trytond/ir/action.py | 3 +- trytond/ir/cron.py | 3 +- trytond/ir/ui/menu.py | 5 +- trytond/model/browse.py | 11 +-- trytond/model/fields/many2one.py | 26 ------- trytond/model/fields/one2many.py | 2 +- trytond/model/fields/property.py | 12 --- trytond/model/fields/reference.py | 11 +-- trytond/model/model.py | 34 ++++----- trytond/model/modelsql.py | 123 ++++++++++++++++++++++++------ trytond/model/modelstorage.py | 73 +++++++++--------- trytond/res/request.py | 9 +-- trytond/res/user.py | 25 +++--- trytond/webdav/webdav.py | 8 +- 15 files changed, 185 insertions(+), 162 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 29458e8c2e..10ba211997 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,5 @@ +* Allow to use related fields (many2one and reference) in read +* Use rec_name function field instead of name_get and name_search * Use a new Pool for objects * Move workflow in ModelWorkflow and the workflow module * Remove Service and LocalService objects diff --git a/trytond/ir/action.py b/trytond/ir/action.py index 214d3bab8b..61cbe8e8fd 100644 --- a/trytond/ir/action.py +++ b/trytond/ir/action.py @@ -257,8 +257,7 @@ class ActionReport(OSV): ('format', '=', 'odt'), ], limit=1, context=context) if formats: - return format_obj.name_get(cursor, user, formats[0], - context=context)[0] + return formats[0] return False def default_module(self, cursor, user, context=None): diff --git a/trytond/ir/cron.py b/trytond/ir/cron.py index c7a6e864f0..56e105962b 100644 --- a/trytond/ir/cron.py +++ b/trytond/ir/cron.py @@ -72,8 +72,7 @@ class Cron(OSV): return 5 def default_user(self, cursor, user, context=None): - user_obj = self.pool.get('res.user') - return user_obj.name_get(cursor, user, user, context=context)[0] + return user def default_interval_number(self, cursor, user, context=None): return 1 diff --git a/trytond/ir/ui/menu.py b/trytond/ir/ui/menu.py index 9737a6924b..4a29003d25 100644 --- a/trytond/ir/ui/menu.py +++ b/trytond/ir/ui/menu.py @@ -148,8 +148,9 @@ class UIMenu(OSV): ], context=ctx) if action_id: action_id = action_id[0] - action_name = action_obj.name_get(cursor, user, action_id, - context=context)[0][1] + action = action_obj.browse(cursor, user, action_id, + context=context) + action_name = action.rec_name else: action_id = 0 action_name = '' diff --git a/trytond/model/browse.py b/trytond/model/browse.py index 4a411a4728..484371eff7 100644 --- a/trytond/model/browse.py +++ b/trytond/model/browse.py @@ -118,18 +118,11 @@ class BrowseRecord(object): continue model = self._model.pool.get(j.model_name) if j._type in ('many2one',): - if data[i]: - if isinstance(data[i][0], (list, tuple)): - ids2 = data[i][0][0] - else: - ids2 = data[i][0] - else: - ids2 = data[i] - if ids2 is False: + if not data[i]: data[i] = BrowseRecordNull() else: data[i] = BrowseRecord(self._cursor, self._user, - ids2, model, self._cache, + data[i], model, self._cache, context=self._context) elif j._type in ('one2many', 'many2many') and len(data[i]): data[i] = BrowseRecordList([BrowseRecord(self._cursor, diff --git a/trytond/model/fields/many2one.py b/trytond/model/fields/many2one.py index ee61aea1a5..334d880c9c 100644 --- a/trytond/model/fields/many2one.py +++ b/trytond/model/fields/many2one.py @@ -14,29 +14,3 @@ class Many2One(Field): self.left = left self.right = right self.ondelete = ondelete - - def get(self, cursor, user, ids, model, name, values=None, context=None): - if values is None: - values = {} - res = {} - for i in values: - res[i['id']] = i[name] - for i in ids: - res.setdefault(i, '') - try: - model = model.pool.get(self.model_name) - except KeyError: - return res - record_names = {} - for record_id, name in model.name_get(cursor, user, - [isinstance(x, (list, tuple)) and x[0] or x - for x in res.values() if x], - context=context): - record_names[record_id] = name - - for i in res.keys(): - if res[i] and res[i] in record_names: - res[i] = (res[i], record_names[res[i]]) - else: - res[i] = False - return res diff --git a/trytond/model/fields/one2many.py b/trytond/model/fields/one2many.py index ed2ac410d7..5d995fe0c9 100644 --- a/trytond/model/fields/one2many.py +++ b/trytond/model/fields/one2many.py @@ -25,7 +25,7 @@ class One2Many(Field): [(self.field, 'in', sub_ids)], context=context) for i in model.pool.get(self.model_name).read(cursor, user, ids2, [self.field], context=context): - res[i[self.field][0]].append( i['id'] ) + res[i[self.field]].append(i['id']) return res def set(self, cursor, user, record_id, model, name, values, context=None): diff --git a/trytond/model/fields/property.py b/trytond/model/fields/property.py index cf897243b4..2809327e65 100644 --- a/trytond/model/fields/property.py +++ b/trytond/model/fields/property.py @@ -14,18 +14,6 @@ class Property(Function): property_obj = model.pool.get('ir.property') res = property_obj.get(cursor, user, name, model._name, ids, context=context) - - if self.model_name: - model = model.pool.get(self.model_name) - record_names = {} - for record_id, record_name in model.name_get(cursor, user, - [x for x in res.values() if x], context=context): - record_names[record_id] = record_name - for i in ids: - if res.get(i) and res[i] in record_names: - res[i] = (res[i], record_names[res[i]]) - else: - res[i] = False return res diff --git a/trytond/model/fields/reference.py b/trytond/model/fields/reference.py index c134a8e146..f1726f55d8 100644 --- a/trytond/model/fields/reference.py +++ b/trytond/model/fields/reference.py @@ -46,14 +46,5 @@ class Reference(Field): ('id', '=', ref_id), ], context=ctx): ref_id = False - if ref_id: - ref_name = ref_obj.name_get(cursor, user, ref_id, - context=context) - if ref_name: - ref_name = ref_name[0][1] - else: - ref_name = '' - else: - ref_name = '' - res[i] = ref_model + ',(' + str(ref_id) + ',"' + ref_name + '")' + res[i] = ref_model + ',' + str(ref_id) return res diff --git a/trytond/model/model.py b/trytond/model/model.py index bde7daa6df..3757b9de63 100644 --- a/trytond/model/model.py +++ b/trytond/model/model.py @@ -433,13 +433,13 @@ class Model(object): property_obj = self.pool.get('ir.property') value[field] = property_obj.get(cursor, user, field, self._name) - if self._columns[field]._type in ('many2one',) \ - and value[field]: - obj = self.pool.get(self._columns[field].model_name) - if isinstance(value[field], (int, long)) \ - and hasattr(obj, 'name_get'): - value[field] = obj.name_get(cursor, user, - value[field], context=context)[0] + if self._columns[field]._type in ('many2one',) \ + and value.get(field): + obj = self.pool.get(self._columns[field].model_name) + if 'rec_name' in obj._columns: + value[field + '.rec_name'] = obj.browse(cursor, + user, value[field], + context=context).rec_name # get the default values set by the user and override the default # values defined in the object @@ -457,10 +457,10 @@ class Model(object): ('id', '=', field_value), ]): continue - if isinstance(field_value, (int, long)) \ - and hasattr(obj, 'name_get'): - field_value = obj.name_get(cursor, user, field_value, - context=context)[0] + if 'rec_name' in obj._columns: + value[field + '.rec_name'] = obj.browse(cursor, + user, field_value, + context=context).rec_name if fld_def._type in ('many2many'): obj = self.pool.get(fld_def.model_name) field_value2 = [] @@ -487,13 +487,11 @@ class Model(object): ('id', '=', field_value[i][field2]), ]): continue - if isinstance(field_value[i][field2], - (int, long)) \ - and hasattr(obj2, 'name_get'): - field_value[i][field2] = obj2.name_get( - cursor, user, - field_value[i][field2], - context=context)[0] + if 'rec_name' in obj2._columns: + field_value[i][field2 + '.rec_name'] = \ + obj2.browse(cursor, user, + field_value[i][field2], + context=context).rec_name # TODO add test for many2many and one2many field_value2[i][field2] = field_value[i][field2] field_value = field_value2 diff --git a/trytond/model/modelsql.py b/trytond/model/modelsql.py index cab4b5f5a9..4f816e997a 100644 --- a/trytond/model/modelsql.py +++ b/trytond/model/modelsql.py @@ -286,9 +286,17 @@ class ModelSQL(ModelStorage): domain1, domain2 = rule_obj.domain_get(cursor, user, self._name, context=context) + fields_related = {} + for field_name in fields_names: + if '.' in field_name: + field, field_related = field_name.split('.', 1) + fields_related.setdefault(field, []) + fields_related[field].append(field_related) + # all inherited fields + all non inherited fields - fields_pre = [x for x in fields_names if (x in self._columns \ - and not hasattr(self._columns[x], 'set')) or \ + fields_pre = [x for x in fields_names + fields_related.keys() \ + if (x in self._columns and \ + not hasattr(self._columns[x], 'set')) or \ (x == '_timestamp')] + \ self._inherits.values() @@ -370,12 +378,19 @@ class ModelSQL(ModelStorage): field = self._inherits[table] inherits_fields = list( set(self._inherit_fields.keys()).intersection( - set(fields_names)).difference( + set(fields_names + fields_related.keys())).difference( set(self._columns.keys()))) if not inherits_fields: continue + inherit_related_fields = [] + for inherit_field in inherits_fields: + if inherit_field in fields_related: + for field_related in fields_related[inherit_field]: + inherit_related_fields.append( + inherit_field + '.' + field_related) res2 = self.pool.get(table).read(cursor, user, - [x[field] for x in res], fields_names=inherits_fields, + [x[field] for x in res], + fields_names=inherits_fields + inherit_related_fields, context=context) res3 = {} @@ -385,13 +400,14 @@ class ModelSQL(ModelStorage): for record in res: record.update(res3[record[field]]) - if field not in fields_names: + if field not in fields_names + fields_related.keys(): del record[field] ids = [x['id'] for x in res] # all non inherited fields for which there is a get attribute - fields_post = [x for x in fields_names if x in self._columns \ + fields_post = [x for x in fields_names + fields_related.keys() \ + if x in self._columns \ and hasattr(self._columns[x], 'get')] func_fields = {} for field in fields_post: @@ -415,6 +431,65 @@ class ModelSQL(ModelStorage): for record in res: record[field] = res2[field][record['id']] + to_del = [] + fields_related2values = {} + for field in fields_related.keys(): + if field not in fields_names: + to_del.append(field) + if field not in self._columns: + continue + fields_related2values.setdefault(field, {}) + if self._columns[field]._type == 'many2one': + obj = self.pool.get(self._columns[field].model_name) + for record in obj.read(cursor, user, [x[field] for x in res + if x[field]], fields_related[field], context=context): + record_id = record['id'] + del record['id'] + fields_related2values[field][record_id] = record + elif self._columns[field]._type == 'reference': + for record in res: + if not record[field]: + continue + model_name, record_id = record[field].split(',', 1) + if not model_name: + continue + record_id = int(record_id) + if not record_id: + continue + obj = self.pool.get(model_name) + record2 = obj.read(cursor, user, record_id, + fields_related[field], context=context) + del record2['id'] + fields_related2values[field][record_id] = record2 + + if to_del or fields_related.keys(): + for record in res: + for field in fields_related.keys(): + if field not in self._columns: + continue + for related in fields_related[field]: + if self._columns[field]._type == 'many2one': + if record[field]: + record[field + '.' + related] = \ + fields_related2values[field][ + record[field]][related] + else: + record[field + '.' + related] = False + elif self._columns[field]._type == 'reference': + if record[field]: + model_name, record_id = record[field].split( + ',', 1) + if not model_name: + continue + record_id = int(record_id) + if not record_id: + continue + record[field + '.' + related] = \ + fields_related2values[field][ + record_id][related] + for field in to_del: + del record[field] + if int_id: return res[0] return res @@ -902,8 +977,9 @@ class ModelSQL(ModelStorage): if isinstance(domain[i][2], basestring): # get the ids of the records of the "distant" resource - ids2 = [x[0] for x in field_obj.name_search(cursor, user, - domain[i][2], [], domain[i][1], context=context)] + ids2 = [x[0] for x in field_obj.search(cursor, user, [ + ('rec_name', domain[i][1], domain[i][2]), + ], context=context)] else: ids2 = domain[i][2] @@ -951,9 +1027,10 @@ class ModelSQL(ModelStorage): # XXX must find a solution for long id list if domain[i][1] in ('child_of', 'not child_of'): if isinstance(domain[i][2], basestring): - ids2 = [x[0] for x in self.pool.get( - field.model_name).name_search(cursor, user, domain[i][2], [], - 'like', context=context)] + field_obj = self.pool.get(field.model_name) + ids2 = [x[0] for x in field_obj.search(cursor, user, [ + ('rec_name', 'ilike', domain[i][2]), + ], context=context)] elif isinstance(domain[i][2], (int, long)): ids2 = [domain[i][2]] else: @@ -994,9 +1071,10 @@ class ModelSQL(ModelStorage): table, domain[i][0])) else: if isinstance(domain[i][2], basestring): - res_ids = [x[0] for x in self.pool.get(field.model_name - ).name_search(cursor, user, domain[i][2], [], - domain[i][1], context=context)] + field_obj = self.pool.get(field.model_name) + res_ids = [x[0] for x in field_obj.search(cursor, user, [ + ('rec_name', domain[i][1], domain[i][2]), + ], context=context)] else: res_ids = domain[i][2] if res_ids == True or res_ids == False: @@ -1023,9 +1101,10 @@ class ModelSQL(ModelStorage): # XXX must find a solution for long id list if domain[i][1] in ('child_of', 'not child_of'): if isinstance(domain[i][2], basestring): - ids2 = [x[0] for x in self.pool.get( - field.model_name).name_search(cursor, user, domain[i][2], - [], 'like', context=context)] + field_obj = self.pool.get(field.model_name) + ids2 = [x[0] for x in field_obj.search(cursor, user, [ + ('rec_name', 'like', domain[i][2]), + ], context=context)] elif isinstance(domain[i][2], (int, long)): ids2 = [domain[i][2]] else: @@ -1082,11 +1161,11 @@ class ModelSQL(ModelStorage): ids2, table, domain[i][0]), table) else: if isinstance(domain[i][2], basestring): - res_ids = self.pool.get(field.model_name).name_search(cursor, - user, domain[i][2], [], domain[i][1], - context=context) - domain[i] = (domain[i][0], 'in', [x[0] for x in res_ids], - table) + field_obj = self.pool.get(field.model_name) + res_ids = field_obj.search(cursor, user, [ + (field_obj._rec_name, domain[i][1], domain[i][2]), + ], context=context) + domain[i] = (domain[i][0], 'in', res_ids, table) else: domain[i] += (table,) i += 1 diff --git a/trytond/model/modelstorage.py b/trytond/model/modelstorage.py index 35e5063e7d..50f5802e71 100644 --- a/trytond/model/modelstorage.py +++ b/trytond/model/modelstorage.py @@ -38,6 +38,8 @@ class ModelStorage(Model): create_date = fields.DateTime('Create Date', required=True, readonly=True) write_uid = fields.Many2One('res.user', 'Write User', readonly=True) write_date = fields.DateTime('Write Date', readonly=True) + rec_name = fields.Function('get_rec_name', type='char', string='Name', + fnct_search='search_rec_name') def __init__(self): super(ModelStorage, self).__init__() @@ -50,8 +52,6 @@ class ModelStorage(Model): 'search', 'search_count', 'search_read', - 'name_get', - 'name_search', 'export_data', 'import_data', ] @@ -343,6 +343,8 @@ class ModelStorage(Model): ids = self.search(cursor, user, domain, offset=offset, limit=limit, order=order, context=context) if limit == 1: + if not ids: + return [] ids = ids[0] return self.read(cursor, user, ids, fields_names=fields_names, context=context) @@ -379,46 +381,43 @@ class ModelStorage(Model): return domain return process(domain) - def name_get(self, cursor, user, ids, context=None): + def get_rec_name(self, cursor, user, ids, name, arg, context=None): ''' - Return a list of tuple for each ids. - The tuple contains the id and the name of the record. + Return a dictionary with id as key and rec_name as value. + It is used by the Function field rec_name :param cursor: the database cursor :param user: the user id - :param ids: a list of ids or an id + :param ids: a list of ids + :param name: the name of the Function field + :param arg: the argument of the Function field :param context: the context - :return: a list of tuple for each ids + :return: a dictionary ''' if not ids: - return [] - if isinstance(ids, (int, long)): - ids = [ids] - return [(r['id'], unicode(r[self._rec_name])) for r in self.read(cursor, - user, ids, [self._rec_name], context=context)] + return {} + res = {} + for record in self.browse(cursor, user, ids, context=context): + res[record.id] = unicode(record[self._rec_name]) + return res - def name_search(self, cursor, user, name='', args=None, operator='ilike', - context=None, limit=None): + def search_rec_name(self, cursor, user, name, args, context=None): ''' - Return a list of tuple like in name_get - where the name and the domain match. + Return a list of arguments for search on rec_name :param cursor: the database cursor :param user: the user id - :param name: the name searched - :param args: a domain like in search - :param operator: the operator to use to compare name with _rec_name + :param name: the name of the Function field + :param args: the list of arguments :param context: the context - :param limit: a integer to limit the result - :return: a list of tuple for each ids like in name_get + :return: a list of arguments ''' - if args is None: - args = [] - if name: - args = ['AND', args, (self._rec_name, operator, name)] - ids = self.search(cursor, user, args, limit=limit, context=context) - res = self.name_get(cursor, user, ids, context=context) - return res + args2 = [] + i = 0 + while i < len(args): + args2.append((self._rec_name, args[i][1], args[i][2])) + i += 1 + return args2 def browse(self, cursor, user, ids, context=None): ''' @@ -588,9 +587,10 @@ class ModelStorage(Model): if line[i]: relation = \ fields_def[field[len(prefix)]]['relation'] - res2 = self.pool.get(relation).name_search(cursor, - user, line[i], [], operator='=') - res = (res2 and res2[0][0]) or False + relation_obj = self.pool.get(relation) + res = relation_obj.search(cursor, user, [ + ('rec_name', '=', line[i]), + ], limit=1, context=context) if not res: warning += ('Relation not found: ' + line[i] + \ ' on ' + relation + ' !\n') @@ -603,17 +603,18 @@ class ModelStorage(Model): relation = \ fields_def[field[len(prefix)]]['relation'] for word in line[i].split(','): - res2 = self.pool.get(relation).name_search( - cursor, user, word, [], operator='=') - res3 = (res2 and res2[0][0]) or False - if not res3: + relation_obj = self.pool.get(relation) + res2 = relation_obj.search(cursor, user, [ + ('rec_name', '=', word), + ], limit=1, context=context) + if not res2: warning += ('Relation not found: ' + \ line[i] + ' on '+relation + ' !\n') logger.warning( 'Relation not found: ' + line[i] + \ ' on '+relation + ' !\n') else: - res.append(res3) + res.append(res2) if len(res): res = [('set', res)] else: diff --git a/trytond/res/request.py b/trytond/res/request.py index 4a1612c7b4..423af7181d 100644 --- a/trytond/res/request.py +++ b/trytond/res/request.py @@ -59,8 +59,7 @@ class Request(OSV): 'History', readonly=True) def default_act_from(self, cursor, user, context=None): - user_obj = self.pool.get('res.user') - return user_obj.name_get(cursor, user, user, context=context)[0] + return user def default_state(self, cursor, user, context=None): return 'draft' @@ -207,12 +206,10 @@ class RequestHistory(OSV): return 'No Name' def default_act_from(self, cursor, user, context=None): - user_obj = self.pool.get('res.user') - return user_obj.name_get(cursor, user, user, context=context)[0] + return user def default_act_to(self, cursor, user, context=None): - user_obj = self.pool.get('res.user') - return user_obj.name_get(cursor, user, user, context=context)[0] + return user def default_date_sent(self, cursor, user, context=None): return datetime.datetime.now() diff --git a/trytond/res/user.py b/trytond/res/user.py index 35402f7a52..1f783896b3 100644 --- a/trytond/res/user.py +++ b/trytond/res/user.py @@ -157,18 +157,19 @@ class User(OSV): val['password'] = 'x' * 10 return res - def name_search(self, cursor, user, name='', args=None, operator='ilike', - context=None, limit=80): - if args is None: - args = [] - ids = [] - if name: - ids = self.search(cursor, user, [('login', '=', name)] + args, - limit=limit, context=context) - if not ids: - ids = self.search(cursor, user, [('name', operator, name)] + args, - limit=limit, context=context) - return self.name_get(cursor, user, ids, context=context) + def search_rec_name(self, cursor, user, name, args, context=None): + args2 = [] + i = 0 + while i < len(args): + ids = self.search(cursor, user, [ + ('login', '=', args[i][2]), + ], context=context) + if len(ids) == 1: + args2.append(('id', '=', ids[0])) + else: + args2.append((self._rec_name, args[i][1], args[i][2])) + i += 1 + return args2 def copy(self, cursor, user, ids, default=None, context=None): if default is None: diff --git a/trytond/webdav/webdav.py b/trytond/webdav/webdav.py index c3e3e64267..9ee3bf6a18 100644 --- a/trytond/webdav/webdav.py +++ b/trytond/webdav/webdav.py @@ -250,14 +250,14 @@ class Collection(OSV): return res model_ids = model_obj.search(cursor, user, eval(collection.domain or "[]"), context=context) - for child_id, child_name in model_obj.name_get(cursor, user, + for child in model_obj.browse(cursor, user, model_ids, context=context): - if '/' in child_name: + if '/' in child.rec_name: continue - res.append(child_name + '-' + str(child_id)) + res.append(child.rec_name + '-' + str(child.id)) if cache is not None: cache.setdefault(model_obj._name, {}) - cache[model_obj._name][child_id] = {} + cache[model_obj._name][child.id] = {} return res else: for child in collection.childs: