diff --git a/trytond/backend/__init__.py b/trytond/backend/__init__.py new file mode 100644 index 0000000000..0ce3f52a4e --- /dev/null +++ b/trytond/backend/__init__.py @@ -0,0 +1,4 @@ +#This file is part of Tryton. The COPYRIGHT file at the top level of +#this repository contains the full copyright notices and license terms. + +from postgresql import * diff --git a/trytond/backend/postgresql/__init__.py b/trytond/backend/postgresql/__init__.py new file mode 100644 index 0000000000..1b519b4db2 --- /dev/null +++ b/trytond/backend/postgresql/__init__.py @@ -0,0 +1,4 @@ +#This file is part of Tryton. The COPYRIGHT file at the top level of +#this repository contains the full copyright notices and license terms. + +from fields import * diff --git a/trytond/backend/postgresql/fields.py b/trytond/backend/postgresql/fields.py new file mode 100644 index 0000000000..e80b671fe4 --- /dev/null +++ b/trytond/backend/postgresql/fields.py @@ -0,0 +1,193 @@ +#This file is part of Tryton. The COPYRIGHT file at the top level of +#this repository contains the full copyright notices and license terms. +import sha +import psycopg2 + + +class Field(object): + symbol_c = '%s' + + @staticmethod + def symbol_f(value): + if value is None or value == False: + return None + elif isinstance(value, str): + return unicode(value, 'utf-8') + elif isinstance(value, unicode): + return value + return unicode(value) + + @staticmethod + def sql_type(field): + return None + + +class Boolean(Field): + + @staticmethod + def symbol_f(value): + return value and 'True' or 'False' + + @staticmethod + def sql_type(field): + return ('bool', 'bool') + + +class Integer(Field): + + @staticmethod + def symbol_f(value): + return int(value or 0) + + @staticmethod + def sql_type(field): + return ('int4', 'int4') + + +class BigInteger(Integer): + + @staticmethod + def sql_type(field): + return ('int8', 'int8') + + +class Char(Field): + + + @staticmethod + def sql_type(field): + if field.size: + return ('varchar', 'varchar(%d)' % (field.size,)) + return ('varchar', 'varchar') + + +class Sha(Field): + + @staticmethod + def symbol_f(value): + return value and sha.new(value).hexdigest() or '' + + @staticmethod + def sql_type(field): + return ('varchar', 'varchar(40)') + + +class Text(Field): + + @staticmethod + def sql_type(field): + return ('text', 'text') + + +class Float(Field): + + @staticmethod + def symbol_f(value): + return float(value or 0.0) + + @staticmethod + def sql_type(field): + return ('float8', 'float8') + + +class Numeric(Float): + + @staticmethod + def sql_type(field): + return ('numeric', 'numeric') + + +class Date(Field): + + @staticmethod + def sql_type(field): + return ('date', 'date') + + +class DateTime(Field): + + @staticmethod + def sql_type(field): + return ('timestamp', 'timestamp(0)') + + +class Time(Field): + + @staticmethod + def sql_type(field): + return ('time', 'time') + + +class Binary(Field): + + @staticmethod + def symbol_f(value): + return value and psycopg2.Binary(value) or None + + @staticmethod + def sql_type(field): + return ('bytea', 'bytea') + + +class Selection(Char): + + @staticmethod + def sql_type(field): + return ('varchar', 'varchar') + + +class Reference(Field): + + @staticmethod + def sql_type(field): + return ('varchar', 'varchar') + + +class Many2One(Field): + + @staticmethod + def symbol_f(value): + return value and int(value) or None + + @staticmethod + def sql_type(field): + return ('int4', 'int4') + + +class One2Many(Field): + pass + + +class Many2Many(Field): + pass + + +class Function(Field): + pass + + +class Property(Function): + pass + + +FIELDS = { + 'boolean': Boolean, + 'integer': Integer, + 'biginteger': BigInteger, + 'char': Char, + 'sha': Sha, + 'text': Text, + 'float': Float, + 'numeric': Numeric, + 'date': Date, + 'datetime': DateTime, + 'time': Time, + 'binary': Binary, + 'selection': Selection, + 'reference': Reference, + 'many2one': Many2One, + 'one2many': One2Many, + 'many2many': Many2Many, + 'function': Function, + 'property': Property, +} diff --git a/trytond/model/browse.py b/trytond/model/browse.py index 3914ed493c..6f05fe9aa8 100644 --- a/trytond/model/browse.py +++ b/trytond/model/browse.py @@ -15,7 +15,7 @@ class BrowseRecordList(list): res = [] for record in self: res2 = {} - for field_name, field in record._table._columns.iteritems(): + for field_name, field in record._model._columns.iteritems(): if not isinstance(record[field_name], BrowseRecordList): res2[field_name] = record.get_eval(field_name) res.append(res2) @@ -48,23 +48,19 @@ class BrowseRecord(object): An object that represents record defined by a ORM object. ''' - def __init__(self, cursor, user, object_id, table, cache, context=None): - ''' - table : the object (inherited from orm) - context : a dictionnary with an optionnal context - ''' + def __init__(self, cursor, user, record_id, model, cache, context=None): self._cursor = cursor self._user = user - self._id = object_id - self._table = table - self._table_name = self._table._name + self._id = record_id + self._model = model + self._model_name = self._model._name self._context = context self._language_cache = {} - cache.setdefault(table._name, {}) - self._data = cache[table._name] - if not object_id in self._data: - self._data[object_id] = {'id': object_id} + cache.setdefault(model._name, {}) + self._data = cache[model._name] + if not record_id in self._data: + self._data[record_id] = {'id': record_id} self._cache = cache def __getitem__(self, name): @@ -76,34 +72,31 @@ class BrowseRecord(object): # build the list of fields we will fetch # fetch the definition of the field which was asked for - if name in self._table._columns: - col = self._table._columns[name] - elif name in self._table._inherit_fields: - col = self._table._inherit_fields[name][2] - elif hasattr(self._table, name): - return getattr(self._table, name) + if name in self._model._columns: + col = self._model._columns[name] + elif name in self._model._inherit_fields: + col = self._model._inherit_fields[name][2] + elif hasattr(self._model, name): + return getattr(self._model, name) else: raise Exception('Error', 'Programming error: field "%s" ' \ - 'does not exist in object "%s"!' \ - % (name, self._table._name)) + 'does not exist in model "%s"!' \ + % (name, self._model._name)) - # if the field is a classic one or a many2one, - # we'll fetch all classic and many2one fields - if col._classic_write: + if not hasattr(col, 'set'): # gen the list of "local" (ie not inherited) - # fields which are classic or many2one - ffields = [x for x in self._table._columns.items() \ - if x[1]._classic_write \ + ffields = [x for x in self._model._columns.items() \ + if not hasattr(x[1], 'set') \ and x[0] not in self._data[self._id] \ and ((not x[1].translate \ and x[1]._type not in ('text', 'binary')) \ or x[0] == name)] # gen the list of inherited fields inherits = [(x[0], x[1][2]) for x in \ - self._table._inherit_fields.items()] + self._model._inherit_fields.items()] # complete the field list with the inherited fields # which are classic or many2one - ffields += [x for x in inherits if x[1]._classic_write \ + ffields += [x for x in inherits if not hasattr(x[1], 'set') \ and x[0] not in self._data[self._id] \ and ((not x[1].translate \ and x[1]._type not in ('text', 'binary')) \ @@ -114,15 +107,17 @@ class BrowseRecord(object): ids = [x for x in self._data.keys() \ if not self._data[x].has_key(name)] # read the data - datas = self._table.read(self._cursor, self._user, ids, + datas = self._model.read(self._cursor, self._user, ids, [x[0] for x in ffields], context=self._context) - # create browse records for 'remote' objects + # create browse records for 'remote' models for data in datas: for i, j in ffields: - if not j._obj in self._table.pool.object_name_list(): + if not hasattr(j, 'model_name') \ + or not (j.model_name in \ + self._model.pool.object_name_list()): continue - obj = self._table.pool.get(j._obj) + model = self._model.pool.get(j.model_name) if j._type in ('many2one',): if data[i]: if isinstance(data[i][0], (list, tuple)): @@ -135,12 +130,12 @@ class BrowseRecord(object): data[i] = BrowseRecordNull() else: data[i] = BrowseRecord(self._cursor, self._user, - ids2, obj, self._cache, + ids2, model, self._cache, context=self._context) elif j._type in ('one2many', 'many2many') and len(data[i]): data[i] = BrowseRecordList([BrowseRecord(self._cursor, self._user, - isinstance(x, (list, tuple)) and x[0] or x, obj, + isinstance(x, (list, tuple)) and x[0] or x, model, self._cache, context=self._context) for x in data[i]], self._context) self._data[data['id']].update(data) @@ -151,9 +146,9 @@ class BrowseRecord(object): return self[name] def __contains__(self, name): - return (name in self._table._columns) \ - or (name in self._table._inherit_fields) \ - or hasattr(self._table, name) + return (name in self._model._columns) \ + or (name in self._model._inherit_fields) \ + or hasattr(self._model, name) def __hasattr__(self, name): return name in self @@ -162,13 +157,13 @@ class BrowseRecord(object): return self._id def __str__(self): - return "BrowseRecord(%s, %d)" % (self._table_name, self._id) + return "BrowseRecord(%s, %d)" % (self._model_name, self._id) def __eq__(self, other): - return (self._table_name, self._id) == (other._table_name, other._id) + return (self._model_name, self._id) == (other._model_name, other._id) def __ne__(self, other): - return (self._table_name, self._id) != (other._table_name, other._id) + return (self._model_name, self._id) != (other._model_name, other._id) # we need to define __unicode__ even though we've already defined __str__ # because we have overridden __getattr__ @@ -176,7 +171,7 @@ class BrowseRecord(object): return unicode(str(self)) def __hash__(self): - return hash((self._table_name, self._id)) + return hash((self._model_name, self._id)) def __nonzero__(self): return bool(self._id) @@ -187,18 +182,18 @@ class BrowseRecord(object): self._context = self._context.copy() prev_lang = self._context.get('language') or 'en_US' self._context['language'] = lang - for table in self._cache: - for obj_id in self._cache[table]: + for model in self._cache: + for record_id in self._cache[model]: self._language_cache.setdefault(prev_lang, - {}).setdefault(table, {}).update( - self._cache[table][obj_id]) + {}).setdefault(model, {}).update( + self._cache[model][record_id]) if lang in self._language_cache \ - and table in self._language_cache[lang] \ - and obj_id in self._language_cache[lang][table]: - self._cache[table][obj_id] = \ - self._language_cache[lang][table][obj_id] + and model in self._language_cache[lang] \ + and record_id in self._language_cache[lang][model]: + self._cache[model][record_id] = \ + self._language_cache[lang][model][record_id] else: - self._cache[table][obj_id] = {'id': obj_id} + self._cache[model][record_id] = {'id': record_id} def get_eval(self, name): res = self[name] @@ -213,23 +208,23 @@ class BrowseRecord(object): class EvalEnvironment(dict): - def __init__(self, record, obj): + def __init__(self, record, model): super(EvalEnvironment, self).__init__() - self.record = record - self.obj = obj + self._record = record + self._model = model def __getitem__(self, item): if item.startswith('_parent_'): field = item[8:] - if field in self.obj._columns: - _obj = self.obj._columns[field]._obj + if field in self._model._columns: + model_name = self._model._columns[field].model_name else: - _obj = self.obj._inherit_fields[field][2]._obj - obj = self.obj.pool.get(_obj) - return EvalEnvironment(self.record[field], obj) - if item in self.obj._columns \ - or item in self.obj._inherit_fields: - return self.record.get_eval(item) + model_name = self._model._inherit_fields[field][2].model_name + model = self._model.pool.get(model_name) + return EvalEnvironment(self._record[field], model) + if item in self._model._columns \ + or item in self._model._inherit_fields: + return self._record.get_eval(item) return super(EvalEnvironment, self).__getitem__(item) def __getattr__(self, item): @@ -243,4 +238,4 @@ class EvalEnvironment(dict): return super(EvalEnvironment, self).get(item) def __nonzero__(self): - return bool(self.record) + return bool(self._record) diff --git a/trytond/model/fields/__init__.py b/trytond/model/fields/__init__.py new file mode 100644 index 0000000000..4fc8f8d6f9 --- /dev/null +++ b/trytond/model/fields/__init__.py @@ -0,0 +1,19 @@ +#This file is part of Tryton. The COPYRIGHT file at the top level of +#this repository contains the full copyright notices and license terms. + +from boolean import * +from integer import * +from char import * +from sha import * +from text import * +from float import * +from numeric import * +from date import * +from binary import * +from selection import * +from reference import * +from many2one import * +from one2many import * +from many2many import * +from function import * +from property import * diff --git a/trytond/model/fields/binary.py b/trytond/model/fields/binary.py new file mode 100644 index 0000000000..93526bb269 --- /dev/null +++ b/trytond/model/fields/binary.py @@ -0,0 +1,18 @@ +#This file is part of Tryton. The COPYRIGHT file at the top level of +#this repository contains the full copyright notices and license terms. + +from trytond.model.fields.field import Field + + +class Binary(Field): + _type = 'binary' + + 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] and str(i[name]) or None + for i in ids: + res.setdefault(i, None) + return res diff --git a/trytond/model/fields/boolean.py b/trytond/model/fields/boolean.py new file mode 100644 index 0000000000..fddb6d3725 --- /dev/null +++ b/trytond/model/fields/boolean.py @@ -0,0 +1,8 @@ +#This file is part of Tryton. The COPYRIGHT file at the top level of +#this repository contains the full copyright notices and license terms. + +from trytond.model.fields.field import Field + + +class Boolean(Field): + _type = 'boolean' diff --git a/trytond/model/fields/char.py b/trytond/model/fields/char.py new file mode 100644 index 0000000000..1f68d4dba8 --- /dev/null +++ b/trytond/model/fields/char.py @@ -0,0 +1,12 @@ +#This file is part of Tryton. The COPYRIGHT file at the top level of +#this repository contains the full copyright notices and license terms. + +from trytond.model.fields.field import Field + + +class Char(Field): + _type = 'char' + + def __init__(self, string='', size=None, **args): + super(Char, self).__init__(string=string, **args) + self.size = size diff --git a/trytond/model/fields/date.py b/trytond/model/fields/date.py new file mode 100644 index 0000000000..345f511ac1 --- /dev/null +++ b/trytond/model/fields/date.py @@ -0,0 +1,16 @@ +#This file is part of Tryton. The COPYRIGHT file at the top level of +#this repository contains the full copyright notices and license terms. + +from trytond.model.fields.field import Field + + +class Date(Field): + _type = 'date' + + +class DateTime(Field): + _type = 'datetime' + + +class Time(Field): + _type = 'time' diff --git a/trytond/model/fields/field.py b/trytond/model/fields/field.py new file mode 100644 index 0000000000..d967d1d751 --- /dev/null +++ b/trytond/model/fields/field.py @@ -0,0 +1,26 @@ +#This file is part of Tryton. The COPYRIGHT file at the top level of +#this repository contains the full copyright notices and license terms. + + +class Field(object): + _type = None + + def __init__(self, string='', help='', required=False, readonly=False, + domain=None, states=None, priority=0, change_default=False, + translate=False, select=0, on_change=None, on_change_with=None, + depends=None, order_field=None, context=None): + self.string = string + self.help = help + self.required = required + self.readonly = readonly + self.domain = domain or [] + self.states = states or {} + self.priority = priority + self.change_default = change_default + self.translate = translate + self.select = select + self.on_change = on_change + self.on_change_with = on_change_with + self.depends = depends or [] + self.order_field = order_field + self.context = context or '' diff --git a/trytond/model/fields/float.py b/trytond/model/fields/float.py new file mode 100644 index 0000000000..48c52a054d --- /dev/null +++ b/trytond/model/fields/float.py @@ -0,0 +1,12 @@ +#This file is part of Tryton. The COPYRIGHT file at the top level of +#this repository contains the full copyright notices and license terms. + +from trytond.model.fields.field import Field + + +class Float(Field): + _type = 'float' + + def __init__(self, string='', digits=None, **args): + super(Float, self).__init__(string=string, **args) + self.digits = digits diff --git a/trytond/model/fields/function.py b/trytond/model/fields/function.py new file mode 100644 index 0000000000..5f0fbbf0a9 --- /dev/null +++ b/trytond/model/fields/function.py @@ -0,0 +1,59 @@ +#This file is part of Tryton. The COPYRIGHT file at the top level of +#this repository contains the full copyright notices and license terms. + +from trytond.model.fields.field import Field +import inspect + + +class Function(Field): + _type = 'float' + + def __init__(self, fnct, arg=None, fnct_inv='', fnct_inv_arg=None, + type='float', fnct_search='', model_name=None, selection=None, + digits=None, relation=None, add_remove=None, **args): + super(Function, self).__init__(**args) + self.model_name = model_name + self.fnct = fnct + self.arg = arg + self.fnct_inv = fnct_inv + self.fnct_inv_arg = fnct_inv_arg + if not self.fnct_inv: + self.readonly = True + self._type = type + self.fnct_search = fnct_search + self.selection = selection + self.digits = digits + if relation: + self.model_name = relation + self.add_remove = add_remove + + def search(self, cursor, user, model, name, args, context=None): + if not self.fnct_search: + return [] + return getattr(model, self.fnct_search)(cursor, user, name, args, + context=context) + + def get(self, cursor, user, ids, model, name, values=None, context=None): + if isinstance(name, list): + names = name + # Test is the function works with a list of names + if 'names' in inspect.getargspec(getattr(model, self.fnct))[0]: + return getattr(model, self.fnct)(cursor, user, ids, names, + self.arg, context=context) + res = {} + for name in names: + res[name] = getattr(model, self.fnct)(cursor, user, ids, name, + self.arg, context=context) + return res + else: + # Test is the function works with a list of names + if 'names' in inspect.getargspec(getattr(model, self.fnct))[0]: + name = [name] + return getattr(model, self.fnct)(cursor, user, ids, name, self.arg, + context=context) + + + def set(self, cursor, user, record_id, model, name, values, context=None): + if self.fnct_inv: + getattr(model, self.fnct_inv)(cursor, user, record_id, name, values, + self.fnct_inv_arg, context=context) diff --git a/trytond/model/fields/integer.py b/trytond/model/fields/integer.py new file mode 100644 index 0000000000..a80d899d4a --- /dev/null +++ b/trytond/model/fields/integer.py @@ -0,0 +1,12 @@ +#This file is part of Tryton. The COPYRIGHT file at the top level of +#this repository contains the full copyright notices and license terms. + +from trytond.model.fields.field import Field + + +class Integer(Field): + _type = 'integer' + + +class BigInteger(Field): + _type = 'biginteger' diff --git a/trytond/model/fields/many2many.py b/trytond/model/fields/many2many.py new file mode 100644 index 0000000000..08f8905c1b --- /dev/null +++ b/trytond/model/fields/many2many.py @@ -0,0 +1,128 @@ +#This file is part of Tryton. The COPYRIGHT file at the top level of +#this repository contains the full copyright notices and license terms. + +from trytond.model.fields.field import Field + + +class Many2Many(Field): + _type = 'many2many' + + def __init__(self, model_name, relation_name, origin, target, string='', + order=None, ondelete_origin='CASCADE', ondelete_target='RESTRICT', + **args): + super(Many2Many, self).__init__(string=string, **args) + self.model_name = model_name + self.relation_name = relation_name + self.origin = origin + self.target = target + self.order = order + self.ondelete_origin = ondelete_origin + self.ondelete_target = ondelete_target + + def get(self, cursor, user, ids, model, name, values=None, context=None): + if values is None: + values = {} + res = {} + if not ids: + return res + for i in ids: + res[i] = [] + ids_s = ','.join([str(x) for x in ids]) + model = model.pool.get(self.model_name) + + domain1, domain2 = model.pool.get('ir.rule').domain_get(cursor, + user, model._name, context=context) + if domain1: + domain1 = ' and ' + domain1 + + #TODO fix order: can have many fields + cursor.execute('SELECT ' + self.relation_name + '.' + self.target + ', ' + \ + self.relation_name + '.' + self.origin + ' ' \ + 'FROM "' + self.relation_name + '" , "' + model._table + '" ' \ + 'WHERE ' + \ + self.relation_name + '.' + self.origin + ' IN (' + ids_s + ') ' \ + 'AND ' + self.relation_name + '.' + self.target + ' = ' + \ + model._table + '.id ' + domain1 + \ + ' ORDER BY ' + \ + ','.join([model._table + '.' + x[0] + ' ' + x[1] \ + for x in (self.order or model._order)]), domain2) + for i in cursor.fetchall(): + res[i[1]].append(i[0]) + return res + + def set(self, cursor, user, record_id, model, name, values, context=None): + if not values: + return + model = model.pool.get(self.model_name) + for act in values: + if act[0] == 'create': + idnew = model.create(cursor, user, act[1], context=context) + cursor.execute('INSERT INTO "' + self.relation_name + '" ' \ + '(' + self.origin + ', ' + self.target + ') ' \ + 'VALUES (%s, %s)', (record_id, idnew)) + elif act[0] == 'write': + model.write(cursor, user, act[1] , act[2], context=context) + elif act[0] == 'delete': + model.delete(cursor, user, act[1], context=context) + elif act[0] == 'unlink': + if isinstance(act[1], (int, long)): + ids = [act[1]] + else: + ids = list(act[1]) + if not ids: + continue + cursor.execute('DELETE FROM "' + self.relation_name + '" ' \ + 'WHERE "' + self.origin + '" = %s ' \ + 'AND "'+ self.target + '" IN (' \ + + ','.join(['%s' for x in ids]) + ')', + [record_id] + ids) + elif act[0] == 'add': + if isinstance(act[1], (int, long)): + ids = [act[1]] + else: + ids = list(act[1]) + if not ids: + continue + cursor.execute('SELECT "' + self.target + '" ' \ + 'FROM "' + self.relation_name + '" ' \ + 'WHERE "' + self.origin + '" = %s ' \ + 'AND "' + self.target + '" IN (' + \ + ','.join(['%s' for x in ids]) + ')', + [record_id] + ids) + existing_ids = [] + for row in cursor.fetchall(): + existing_ids.append(row[0]) + new_ids = [x for x in ids if x not in existing_ids] + for new_id in new_ids: + cursor.execute('INSERT INTO "' + self.relation_name + '" ' \ + '("' + self.origin + '", "' + self.target + '") ' \ + 'VALUES (%s, %s)', (record_id, new_id)) + elif act[0] == 'unlink_all': + cursor.execute('UPDATE "' + self.relation_name + '" ' \ + 'SET "' + self.target + '" = NULL ' \ + 'WHERE "' + self.target + '" = %s', (record_id,)) + elif act[0] == 'set': + if not act[1]: + ids = [] + else: + ids = list(act[1]) + domain1, domain2 = model.pool.get('ir.rule').domain_get(cursor, + user, model._name, context=context) + if domain1: + domain1 = ' AND ' + domain1 + cursor.execute('DELETE FROM "' + self.relation_name + '" ' \ + 'WHERE "' + self.origin + '" = %s ' \ + 'AND "' + self.target + '" IN (' \ + 'SELECT ' + self.relation_name + '.' + self.target + ' ' \ + 'FROM "' + self.relation_name + '", "' + model._table + '" ' \ + 'WHERE ' + self.relation_name + '.' + self.origin + ' = %s ' \ + 'AND ' + self.relation_name + '.' + self.target + ' = ' + \ + model._table + '.id ' + domain1 + ')', + [record_id, record_id] + domain2) + + for new_id in ids: + cursor.execute('INSERT INTO "' + self.relation_name + '" ' \ + '("' + self.origin + '", "' + self.target + '") ' \ + 'VALUES (%s, %s)', (record_id, new_id)) + else: + raise Exception('Bad arguments') diff --git a/trytond/model/fields/many2one.py b/trytond/model/fields/many2one.py new file mode 100644 index 0000000000..ee61aea1a5 --- /dev/null +++ b/trytond/model/fields/many2one.py @@ -0,0 +1,42 @@ +#This file is part of Tryton. The COPYRIGHT file at the top level of +#this repository contains the full copyright notices and license terms. + +from trytond.model.fields.field import Field + + +class Many2One(Field): + _type = 'many2one' + + def __init__(self, model_name, string='', left=None, right=None, + ondelete='SET NULL', **args): + super(Many2One, self).__init__(string=string, **args) + self.model_name = model_name + 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/numeric.py b/trytond/model/fields/numeric.py new file mode 100644 index 0000000000..659ea99e4e --- /dev/null +++ b/trytond/model/fields/numeric.py @@ -0,0 +1,8 @@ +#This file is part of Tryton. The COPYRIGHT file at the top level of +#this repository contains the full copyright notices and license terms. + +from trytond.model.fields.float import Float + + +class Numeric(Float): + _type = 'numeric' diff --git a/trytond/model/fields/one2many.py b/trytond/model/fields/one2many.py new file mode 100644 index 0000000000..ed2ac410d7 --- /dev/null +++ b/trytond/model/fields/one2many.py @@ -0,0 +1,92 @@ +#This file is part of Tryton. The COPYRIGHT file at the top level of +#this repository contains the full copyright notices and license terms. + +from trytond.model.fields.field import Field + + +class One2Many(Field): + _type = 'one2many' + + def __init__(self, model_name, field, string='', order_field=None, + add_remove=None, **args): + super(One2Many, self).__init__(string=string, **args) + self.model_name = model_name + self.field = field + self.add_remove = add_remove + + def get(self, cursor, user, ids, model, name, values=None, context=None): + res = {} + for i in ids: + res[i] = [] + ids2 = [] + for i in range(0, len(ids), cursor.IN_MAX): + sub_ids = ids[i:i + cursor.IN_MAX] + ids2 += model.pool.get(self.model_name).search(cursor, user, + [(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'] ) + return res + + def set(self, cursor, user, record_id, model, name, values, context=None): + if not values: + return + model = model.pool.get(self.model_name) + for act in values: + if act[0] == 'create': + act[1][self.field] = record_id + model.create(cursor, user, act[1], context=context) + elif act[0] == 'write': + act[2][self.field] = record_id + model.write(cursor, user, act[1] , act[2], context=context) + elif act[0] == 'delete': + model.delete(cursor, user, act[1], context=context) + elif act[0] == 'unlink': + if isinstance(act[1], (int, long)): + ids = [act[1]] + else: + ids = list(act[1]) + if not ids: + continue + ids = model.search(cursor, user, [ + (self.field, '=', record_id), + ('id', 'in', ids), + ], context=context) + model.write(cursor, user, ids, { + self.field: False, + }, context=context) + elif act[0] == 'add': + if isinstance(act[1], (int, long)): + ids = [act[1]] + else: + ids = list(act[1]) + if not ids: + continue + ids = model.write(cursor, user, ids, { + self.field: record_id, + }, context=context) + elif act[0] == 'unlink_all': + ids = model.search(cursor, user, [ + (self.field, '=', record_id), + ], context=context) + model.write(cursor, user, ids, { + self.field: False, + }, context=context) + elif act[0] == 'set': + if not act[1]: + ids = [0] + else: + ids = list(act[1]) + ids2 = model.search(cursor, user, [ + (self.field, '=', record_id), + ('id', 'not in', ids), + ], context=context) + model.write(cursor, user, ids2, { + self.field: False, + }, context=context) + if act[1]: + model.write(cursor, user, ids, { + self.field: record_id, + }, context=context) + else: + raise Exception('Bad arguments') diff --git a/trytond/model/fields/property.py b/trytond/model/fields/property.py new file mode 100644 index 0000000000..cf897243b4 --- /dev/null +++ b/trytond/model/fields/property.py @@ -0,0 +1,36 @@ +#This file is part of Tryton. The COPYRIGHT file at the top level of +#this repository contains the full copyright notices and license terms. + +from trytond.model.fields.function import Function + + +class Property(Function): + + def __init__(self, **args): + super(Property, self).__init__('', **args) + self.readonly = False + + def get(self, cursor, user, ids, model, name, values=None, context=None): + 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 + + + def set(self, cursor, user, record_id, model, name, value, context=None): + property_obj = model.pool.get('ir.property') + return property_obj.set(cursor, user, name, model._name, record_id, + (value and (self.model_name or '') + ',' + str(value)) or False, + context=context) diff --git a/trytond/model/fields/reference.py b/trytond/model/fields/reference.py new file mode 100644 index 0000000000..c134a8e146 --- /dev/null +++ b/trytond/model/fields/reference.py @@ -0,0 +1,59 @@ +#This file is part of Tryton. The COPYRIGHT file at the top level of +#this repository contains the full copyright notices and license terms. + +from trytond.model.fields.field import Field + + +class Reference(Field): + _type = 'reference' + + def __init__(self, string='', selection=[], **args): + super(Reference, self).__init__(string=string, **args) + self.selection = selection + + def get(self, cursor, user, ids, model, name, values=None, context=None): + if context is None: + context = {} + if values is None: + values = {} + res = {} + for i in values: + res[i['id']] = i[name] + for i in ids: + if not (i in res): + res[i] = False + continue + if not res[i]: + continue + ref_model, ref_id = res[i].split(',', 1) + if not ref_model: + continue + ref_obj = model.pool.get(ref_model) + if not ref_obj: + continue + try: + ref_id = eval(ref_id) + except: + pass + try: + ref_id = int(ref_id) + except: + continue + ctx = context.copy() + ctx['active_test'] = False + if ref_id \ + and not ref_obj.search(cursor, user, [ + ('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 + '")' + return res diff --git a/trytond/model/fields/selection.py b/trytond/model/fields/selection.py new file mode 100644 index 0000000000..d2e989452e --- /dev/null +++ b/trytond/model/fields/selection.py @@ -0,0 +1,14 @@ +#This file is part of Tryton. The COPYRIGHT file at the top level of +#this repository contains the full copyright notices and license terms. + +from trytond.model.fields.field import Field + + +class Selection(Field): + _type = 'selection' + + def __init__(self, selection, string='', sort=True, translate=True, **args): + super(Selection, self).__init__(string=string, **args) + self.selection = selection + self.sort = sort + self.translate_selection = translate diff --git a/trytond/model/fields/sha.py b/trytond/model/fields/sha.py new file mode 100644 index 0000000000..2faab39e95 --- /dev/null +++ b/trytond/model/fields/sha.py @@ -0,0 +1,8 @@ +#This file is part of Tryton. The COPYRIGHT file at the top level of +#this repository contains the full copyright notices and license terms. + +from trytond.model.fields.field import Field + + +class Sha(Field): + _type = 'sha' diff --git a/trytond/model/fields/text.py b/trytond/model/fields/text.py new file mode 100644 index 0000000000..052c639c1d --- /dev/null +++ b/trytond/model/fields/text.py @@ -0,0 +1,8 @@ +#This file is part of Tryton. The COPYRIGHT file at the top level of +#this repository contains the full copyright notices and license terms. + +from trytond.model.fields.char import Char + + +class Text(Char): + _type = 'text' diff --git a/trytond/model/model.py b/trytond/model/model.py index bb852aee60..9bee098260 100644 --- a/trytond/model/model.py +++ b/trytond/model/model.py @@ -1,7 +1,7 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. -from trytond.osv import fields +from trytond.model import fields import copy @@ -28,7 +28,7 @@ class Model(object): for attr in dir(self): if attr in ('_columns', '_defaults'): continue - if isinstance(getattr(self, attr), fields.Column): + if isinstance(getattr(self, attr), fields.Field): res[attr] = getattr(self, attr) self.__columns = res return res @@ -200,10 +200,12 @@ class Model(object): "relation, help, module) " \ "VALUES (%s, %s, %s, %s, %s, %s, %s)", (model_id, field_name, field.string, field._type, - field._obj or '', field.help, module_name)) + hasattr(field, 'model_name') and field.model_name or '', + field.help, module_name)) elif fields[field_name]['field_description'] != field.string \ or fields[field_name]['ttype'] != field._type \ - or fields[field_name]['relation'] != (field._obj or '') \ + or fields[field_name]['relation'] != \ + (hasattr(field, 'model_name') and field.model_name or '') \ or fields[field_name]['help'] != field.help: cursor.execute('UPDATE ir_model_field ' \ 'SET field_description = %s, ' \ @@ -211,7 +213,8 @@ class Model(object): 'relation = %s, ' \ 'help = %s ' \ 'WHERE id = %s ', - (field.string, field._type, field._obj or '', + (field.string, field._type, + hasattr(field, 'model_name') and field.model_name or '', field.help, fields[field_name]['id'])) trans_name = self._name + ',' + field_name if trans_name not in trans_fields: @@ -426,7 +429,7 @@ class Model(object): self._name) if self._columns[field]._type in ('many2one',) \ and value[field]: - obj = self.pool.get(self._columns[field]._obj) + 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, @@ -442,7 +445,7 @@ class Model(object): fld_def = (field in self._columns) and self._columns[field] \ or self._inherit_fields[field][2] if fld_def._type in ('many2one',): - obj = self.pool.get(fld_def._obj) + obj = self.pool.get(fld_def.model_name) if not hasattr(obj, 'search') \ or not obj.search(cursor, user, [ ('id', '=', field_value), @@ -453,7 +456,7 @@ class Model(object): field_value = obj.name_get(cursor, user, field_value, context=context)[0] if fld_def._type in ('many2many'): - obj = self.pool.get(fld_def._obj) + obj = self.pool.get(fld_def.model_name) field_value2 = [] for i in range(len(field_value)): if not hasattr(obj, 'search') \ @@ -464,14 +467,15 @@ class Model(object): field_value2.append(field_value[i]) field_value = field_value2 if fld_def._type in ('one2many'): - obj = self.pool.get(fld_def._obj) + obj = self.pool.get(fld_def.model_name) field_value2 = [] for i in range(len(field_value or [])): field_value2.append({}) for field2 in field_value[i]: if obj._columns[field2]._type \ in ('many2one',): - obj2 = self.pool.get(obj._columns[field2]._obj) + obj2 = self.pool.get( + obj._columns[field2].model_name) if not hasattr(obj2, 'search') \ or not obj2.search(cursor, user, [ ('id', '=', field_value[i][field2]), @@ -520,7 +524,7 @@ class Model(object): val.update(getattr(self, 'on_change_' + field)(cursor, user, [], args, context=context)) if self._columns[field]._type in ('one2many',): - obj = self.pool.get(self._columns[field]._obj) + obj = self.pool.get(self._columns[field].model_name) for val2 in res[field]: val2.update(obj._default_on_change(cursor, user, val2, context=context)) @@ -648,12 +652,13 @@ class Model(object): 'many2many', 'many2one', ): - res[field]['relation'] = copy.copy(self._columns[field]._obj) - res[field]['domain'] = copy.copy(self._columns[field]._domain) - res[field]['context'] = copy.copy(self._columns[field]._context) - if res[field]['type'] == 'one2many': + res[field]['relation'] = copy.copy(self._columns[field].model_name) + res[field]['domain'] = copy.copy(self._columns[field].domain) + res[field]['context'] = copy.copy(self._columns[field].context) + if res[field]['type'] == 'one2many' \ + and hasattr(self._columns[field], 'field'): res[field]['relation_field'] = copy.copy( - self._columns[field]._field) + self._columns[field].field) if fields_names: # filter out fields which aren't in the fields_names list diff --git a/trytond/model/modeldb.py b/trytond/model/modeldb.py index 09b1f5520c..37f0a4daf1 100644 --- a/trytond/model/modeldb.py +++ b/trytond/model/modeldb.py @@ -2,9 +2,10 @@ #this repository contains the full copyright notices and license terms. from trytond.model import ModelStorage, OPERATORS -from trytond.osv import fields +from trytond.model import fields from trytond.sql_db import table_handler from trytond.netsvc import LocalService +from trytond.backend import FIELDS import datetime @@ -39,20 +40,22 @@ class ModelDB(ModelStorage): # create/update table in the database table = table_handler(cursor, self._table, self._name, module_name) + datetime_field = FIELDS['datetime'] + integer_field = FIELDS['integer'] logs = ( - ('create_date', 'timestamp', 'TIMESTAMP', - fields.DateTime._symbol_set, lambda *a: datetime.datetime.now()), - ('write_date', 'timestamp', 'TIMESTAMP', - fields.DateTime._symbol_set, None), - ('create_uid', 'int4', - 'INTEGER REFERENCES res_user ON DELETE SET NULL', - fields.Integer._symbol_set, lambda *a: 0), - ('write_uid', 'int4', - 'INTEGER REFERENCES res_user ON DELETE SET NULL', - fields.Integer._symbol_set, None), + ('create_date', datetime_field.sql_type(None), datetime_field.symbol_c, + datetime_field.symbol_f, lambda *a: datetime.datetime.now()), + ('write_date', datetime_field.sql_type(None), datetime_field.symbol_c, + datetime_field.symbol_f, None), + ('create_uid', (integer_field.sql_type(None)[0], + 'INTEGER REFERENCES res_user ON DELETE SET NULL',), + integer_field.symbol_c, integer_field.symbol_f, lambda *a: 0), + ('write_uid', (integer_field.sql_type(None)[0], + 'INTEGER REFERENCES res_user ON DELETE SET NULL'), + integer_field.symbol_c, integer_field.symbol_f, None), ) for log in logs: - table.add_raw_column(log[0], (log[1], log[2]), log[3], + table.add_raw_column(log[0], log[1], (log[2], log[3]), default_fun=log[4], migrate=False) for field_name, field in self._columns.iteritems(): default_fun = None @@ -65,7 +68,7 @@ class ModelDB(ModelStorage): ): continue - if field._classic_write: + if not hasattr(field, 'set'): if field_name in self._defaults: default_fun = self._defaults[field_name] @@ -82,17 +85,20 @@ class ModelDB(ModelStorage): return unpack_result default_fun = unpack_wrapper(default_fun) - table.add_raw_column(field_name, field.sql_type(), - field._symbol_set, default_fun, field.size) + table.add_raw_column(field_name, + FIELDS[field._type].sql_type(field), + (FIELDS[field._type].symbol_c, + FIELDS[field._type].symbol_f), default_fun, + hasattr(field, 'size') and field.size or None) if isinstance(field, (fields.Integer, fields.Float)): table.db_default(field_name, 0) if isinstance(field, fields.Many2One): - if field._obj in ('res.user', 'res.group'): - ref = field._obj.replace('.','_') + if field.model_name in ('res.user', 'res.group'): + ref = field.model_name.replace('.','_') else: - ref = self.pool.get(field._obj)._table + ref = self.pool.get(field.model_name)._table table.add_fk(field_name, ref, field.ondelete) table.index_action( @@ -106,12 +112,12 @@ class ModelDB(ModelStorage): field_name, action=required and 'add' or 'remove') elif isinstance(field, fields.Many2Many): - if field._obj in ('res.user', 'res.group'): - ref = field._obj.replace('.','_') + if field.model_name in ('res.user', 'res.group'): + ref = field.model_name.replace('.','_') else: - ref = self.pool.get(field._obj)._table - table.add_m2m(field_name, ref, field._rel, field.origin, - field.target, field.ondelete_origin, + ref = self.pool.get(field.model_name)._table + table.add_m2m(field_name, ref, field.relation_name, + field.origin, field.target, field.ondelete_origin, field.ondelete_target) elif not isinstance(field, (fields.One2Many, fields.Function)): @@ -119,7 +125,7 @@ class ModelDB(ModelStorage): for field_name, field in self._columns.iteritems(): if isinstance(field, fields.Many2One) \ - and field._obj == self._name \ + and field.model_name == self._name \ and field.left and field.right: self._rebuild_tree(cursor, 0, field_name, False, 0) @@ -204,14 +210,16 @@ class ModelDB(ModelStorage): # Insert record for field in values: - if self._columns[field]._classic_write: + if not hasattr(self._columns[field], 'set'): upd0 = upd0 + ',"' + field + '"' - upd1 = upd1 + ',' + self._columns[field]._symbol_set[0] - upd2.append(self._columns[field]._symbol_set[1](values[field])) + upd1 = upd1 + ',' + FIELDS[self._columns[field]._type].symbol_c + upd2.append(FIELDS[self._columns[field]._type].symbol_f( + values[field])) else: upd_todo.append(field) if field in self._columns \ and hasattr(self._columns[field], 'selection') \ + and self._columns[field].selection \ and values[field]: if self._columns[field]._type == 'reference': val = values[field].split(',')[0] @@ -241,8 +249,8 @@ class ModelDB(ModelStorage): upd_todo.sort(lambda x, y: self._columns[x].priority - \ self._columns[y].priority) for field in upd_todo: - self._columns[field].set(cursor, self, id_new, field, values[field], - user=user, context=context) + self._columns[field].set(cursor, user, id_new, self, field, values[field], + context=context) self._validate(cursor, user, [id_new], context=context) @@ -250,7 +258,7 @@ class ModelDB(ModelStorage): for k in self._columns: field = self._columns[k] if isinstance(field, fields.Many2One) \ - and field._obj == self._name \ + and field.model_name == self._name \ and field.left and field.right: self._update_tree(cursor, user, id_new, k, field.left, field.right) @@ -284,9 +292,8 @@ class ModelDB(ModelStorage): context=context) # all inherited fields + all non inherited fields - # for which the attribute whose name is in load is True fields_pre = [x for x in fields_names if (x in self._columns \ - and getattr(self._columns[x], '_classic_write')) or \ + and not hasattr(self._columns[x], 'set')) or \ (x == '_timestamp')] + \ self._inherits.values() @@ -395,20 +402,20 @@ class ModelDB(ModelStorage): for field in fields_post: if isinstance(self._columns[field], fields.Function) \ and not isinstance(self._columns[field], fields.Property): - key = (self._columns[field]._fnct, self._columns[field]._arg) + key = (self._columns[field].fnct, self._columns[field].arg) func_fields.setdefault(key, []) func_fields[key].append(field) continue # get the value of that field for all records/ids - res2 = self._columns[field].get(cursor, self, ids, field, user, - context=context, values=res) + res2 = self._columns[field].get(cursor, user, ids, self, field, + values=res, context=context) for record in res: record[field] = res2[record['id']] for i in func_fields: field_list = func_fields[i] field = field_list[0] - res2 = self._columns[field].get(cursor, self, ids, field_list, user, - context=context, values=res) + res2 = self._columns[field].get(cursor, user, ids, self, field_list, + values=res, context=context) for field in res2: for record in res: record[field] = res2[field][record['id']] @@ -439,7 +446,7 @@ class ModelDB(ModelStorage): for k in self._columns: field = self._columns[k] if isinstance(field, fields.Many2One) \ - and field._obj == self._name \ + and field.model_name == self._name \ and field.left and field.right: update_tree = True if update_tree: @@ -487,12 +494,12 @@ class ModelDB(ModelStorage): direct = [] for field in values: if field in self._columns: - if self._columns[field]._classic_write: + if not hasattr(self._columns[field], 'set'): if (not self._columns[field].translate) \ or (context.get('language') or 'en_US') == 'en_US': upd0.append('"' + field + '"=' + \ - self._columns[field]._symbol_set[0]) - upd1.append(self._columns[field]._symbol_set[1]( + FIELDS[self._columns[field]._type].symbol_c) + upd1.append(FIELDS[self._columns[field]._type].symbol_f( values[field])) direct.append(field) else: @@ -501,6 +508,7 @@ class ModelDB(ModelStorage): updend.append(field) if field in self._columns \ and hasattr(self._columns[field], 'selection') \ + and self._columns[field].selection \ and values[field]: if self._columns[field]._type == 'reference': val = values[field].split(',')[0] @@ -572,8 +580,8 @@ class ModelDB(ModelStorage): self._columns[y].priority) for field in upd_todo: for select_id in ids: - self._columns[field].set(cursor, self, select_id, field, - values[field], user, context=context) + self._columns[field].set(cursor, user, select_id, self, field, + values[field], context=context) for table in self._inherits: col = self._inherits[table] @@ -599,7 +607,7 @@ class ModelDB(ModelStorage): for k in self._columns: field = self._columns[k] if isinstance(field, fields.Many2One) \ - and field._obj == self._name \ + and field.model_name == self._name \ and field.left and field.right: if field.left in values or field.right in values: raise Exception('ValidateError', 'You can not update fields: ' \ @@ -676,7 +684,7 @@ class ModelDB(ModelStorage): for k in self._columns: field = self._columns[k] if isinstance(field, fields.Many2One) \ - and field._obj == self._name \ + and field.model_name == self._name \ and field.left and field.right: cursor.execute('SELECT id FROM "' + self._table + '" '\ 'WHERE "' + k + '" IN (' \ @@ -886,7 +894,7 @@ class ModelDB(ModelStorage): if len(fargs) > 1: if field._type == 'many2one': domain[i] = (fargs[0], 'inselect', - self.pool.get(field._obj).search(cursor, user, + self.pool.get(field.model_name).search(cursor, user, [(fargs[1], domain[i][1], domain[i][2])], context=context, query_string=True), table) i += 1 @@ -894,7 +902,7 @@ class ModelDB(ModelStorage): else: raise Exception('ValidateError', 'Clause on field "%s" ' \ 'doesn\'t work on "%s"' % (domain[i][0], self._name)) - if field._properties: + if hasattr(field, 'search'): arg = [domain.pop(i)] j = i while j < len(domain): @@ -902,11 +910,10 @@ class ModelDB(ModelStorage): arg.append(domain.pop(j)) else: j += 1 - if field._fnct_search: - domain.extend(field.search(cursor, user, table, - arg[0][0], arg, context=context)) + domain.extend(field.search(cursor, user, table, + arg[0][0], arg, context=context)) elif field._type == 'one2many': - field_obj = self.pool.get(field._obj) + field_obj = self.pool.get(field.model_name) if isinstance(domain[i][2], basestring): # get the ids of the records of the "distant" resource @@ -922,9 +929,9 @@ class ModelDB(ModelStorage): table_query = '(' + table_query + ') AS ' if ids2 == True or ids2 == False: - query1 = 'SELECT "' + field._field + '" ' \ + query1 = 'SELECT "' + field.field + '" ' \ 'FROM ' + table_query + '"' + field_obj._table + '" ' \ - 'WHERE "' + field._field + '" IS NOT NULL' + 'WHERE "' + field.field + '" IS NOT NULL' query2 = table_args clause = 'inselect' if ids2 == False: @@ -934,7 +941,7 @@ class ModelDB(ModelStorage): domain[i] = ('id', '=', '0') else: if len(ids2) < cursor.IN_MAX: - query1 = 'SELECT "' + field._field + '" ' \ + query1 = 'SELECT "' + field.field + '" ' \ 'FROM ' + table_query + '"' + field_obj._table + '" ' \ 'WHERE id IN (' + \ ','.join(['%s' for x in ids2]) + ')' @@ -945,7 +952,7 @@ class ModelDB(ModelStorage): for i in range(0, len(ids2), cursor.IN_MAX): sub_ids2 = ids2[i:i + cursor.IN_MAX] cursor.execute( - 'SELECT "' + field._field + \ + 'SELECT "' + field.field + \ '" FROM ' + table_query + '"' + field_obj._table + '" ' \ 'WHERE id IN (' + \ ','.join(['%s' for x in sub_ids2]) + ')', @@ -960,7 +967,7 @@ class ModelDB(ModelStorage): 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._obj).name_search(cursor, user, domain[i][2], [], + field.model_name).name_search(cursor, user, domain[i][2], [], 'like', context=context)] elif isinstance(domain[i][2], (int, long)): ids2 = [domain[i][2]] @@ -975,16 +982,16 @@ class ModelDB(ModelStorage): context=context) return ids + _rec_get(ids2, table, parent) - if field._obj != table._name: + if field.model_name != table._name: if len(domain[i]) != 4: raise Exception('Error', 'Programming error: ' \ 'child_of on field "%s" is not allowed!' % \ (domain[i][0],)) - ids2 = self.pool.get(field._obj).search(cursor, user, + ids2 = self.pool.get(field.model_name).search(cursor, user, [(domain[i][3], 'child_of', ids2)], context=context) query1 = 'SELECT "' + field.origin + '" ' \ - 'FROM "' + field._rel + '" ' \ + 'FROM "' + field.relation_name + '" ' \ 'WHERE "' + field.target + '" IN (' + \ ','.join(['%s' for x in ids2]) + ') ' \ 'AND "' + field.origin + '" IS NOT NULL' @@ -1002,14 +1009,14 @@ class ModelDB(ModelStorage): table, domain[i][0])) else: if isinstance(domain[i][2], basestring): - res_ids = [x[0] for x in self.pool.get(field._obj + 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)] else: res_ids = domain[i][2] if res_ids == True or res_ids == False: query1 = 'SELECT "' + field.origin + '" ' \ - 'FROM "' + field._rel + '" '\ + 'FROM "' + field.relation_name + '" '\ 'WHERE "' + field.origin + '" IS NOT NULL' query2 = [] clause = 'inselect' @@ -1020,7 +1027,7 @@ class ModelDB(ModelStorage): domain[i] = ('id', '=', '0') else: query1 = 'SELECT "' + field.origin + '" ' \ - 'FROM "' + field._rel + '" ' \ + 'FROM "' + field.relation_name + '" ' \ 'WHERE "' + field.target + '" IN (' + \ ','.join(['%s' for x in res_ids]) + ')' query2 = [str(x) for x in res_ids] @@ -1032,7 +1039,7 @@ class ModelDB(ModelStorage): 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._obj).name_search(cursor, user, domain[i][2], + field.model_name).name_search(cursor, user, domain[i][2], [], 'like', context=context)] elif isinstance(domain[i][2], (int, long)): ids2 = [domain[i][2]] @@ -1047,12 +1054,12 @@ class ModelDB(ModelStorage): context=context) return ids + _rec_get(ids2, table, parent) - if field._obj != table._name: + if field.model_name != table._name: if len(domain[i]) != 4: raise Exception('Error', 'Programming error: ' \ 'child_of on field "%s" is not allowed!' % \ (domain[i][0],)) - ids2 = self.pool.get(field._obj).search(cursor, user, + ids2 = self.pool.get(field.model_name).search(cursor, user, [(domain[i][3], 'child_of', ids2)], context=context) if domain[i][1] == 'child_of': @@ -1090,7 +1097,7 @@ class ModelDB(ModelStorage): ids2, table, domain[i][0]), table) else: if isinstance(domain[i][2], basestring): - res_ids = self.pool.get(field._obj).name_search(cursor, + 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], @@ -1194,10 +1201,11 @@ class ModelDB(ModelStorage): (table._table, ','.join(['%s'] * len(arg2)),)) else: + symbol_c = FIELDS[table._columns[arg[0]]._type]\ + .symbol_c qu1.append(('(%s.%s ' + arg[1] + ' (%s))') % \ (table._table, arg[0], ','.join( - [table._columns[arg[0]].\ - _symbol_set[0]] * len(arg2)))) + [symbol_c] * len(arg2)))) if todel: if table._columns[arg[0]]._type == 'boolean': if arg[1] == 'in': @@ -1263,8 +1271,8 @@ class ModelDB(ModelStorage): add_null = True else: if arg[0] in table._columns: - qu2.append(table._columns[arg[0]].\ - _symbol_set[1](arg[2])) + qu2.append(FIELDS[table._columns[arg[0]]._type].\ + symbol_f(arg[2])) if arg[0] in table._columns: if arg[1] in ('like', 'ilike'): qu1.append('(%s.%s %s %s OR %s.%s %s %s)' % \ @@ -1276,8 +1284,8 @@ class ModelDB(ModelStorage): table._table, arg[0], arg[1], '%s')) else: qu1.append('(%s.%s %s %s)' % (table._table, - arg[0], arg[1], - table._columns[arg[0]]._symbol_set[0])) + arg[0], arg[1], FIELDS[table._columns[ + arg[0]]._type].symbol_c)) else: if arg[1] in ('like', 'ilike'): qu1.append('(%s.%s %s \'%s\' or %s.%s %s \'%s\')' % \ @@ -1310,14 +1318,14 @@ class ModelDB(ModelStorage): if field in self._columns: table_name = self._table - if self._columns[field]._classic_write: + if not hasattr(self._columns[field], 'set'): field_name = field if self._columns[field].order_field: field_name = self._columns[field].order_field if isinstance(self._columns[field], fields.Many2One): - obj = self.pool.get(self._columns[field]._obj) + obj = self.pool.get(self._columns[field].model_name) table_name = obj._table link_field = field field_name = None diff --git a/trytond/model/modelstorage.py b/trytond/model/modelstorage.py index 97990cbc70..2d1aa1a22e 100644 --- a/trytond/model/modelstorage.py +++ b/trytond/model/modelstorage.py @@ -1,7 +1,7 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. from trytond.model import Model -from trytond.osv import fields +from trytond.model import fields from trytond.model.browse import BrowseRecordList, BrowseRecord, BrowseRecordNull from trytond.model.browse import EvalEnvironment import datetime @@ -789,9 +789,9 @@ class ModelStorage(Model): for field_name, field in self._columns.iteritems(): # validate domain if field._type in ('many2one', 'many2many', 'one2many') \ - and field._domain: - relation_obj = self.pool.get(field._obj) - if isinstance(field._domain, basestring): + and field.domain: + relation_obj = self.pool.get(field.model_name) + if isinstance(field.domain, basestring): ctx = context.copy() ctx.update(ctx_pref) for record in records: @@ -801,7 +801,7 @@ class ModelStorage(Model): env['time'] = time env['context'] = context env['active_id'] = record.id - domain = eval(field._domain, env) + domain = eval(field.domain, env) relation_ids = [] if record[field_name]: if field._type in ('many2one',): @@ -832,7 +832,7 @@ class ModelStorage(Model): find_ids = relation_obj.search(cursor, user, [ 'AND', [('id', 'in', relation_ids)], - field._domain, + field.domain, ], context=context) if not set(relation_ids) == set(find_ids): self.raise_user_error(cursor, @@ -853,9 +853,6 @@ class ModelStorage(Model): env['active_id'] = record.id required = eval(field.states['required'], env) if required and not record[field_name]: - print record[field_name] - print field_name - print field.states['required'] self.raise_user_error(cursor, 'required_validation_record', error_args=get_error_args(field_name), @@ -880,7 +877,7 @@ class ModelStorage(Model): else: vals[field] = defaults[field] elif fld_def._type in ('one2many',): - obj = self.pool.get(self._columns[field]._obj) + obj = self.pool.get(self._columns[field].model_name) vals[field] = [] for defaults2 in defaults[field]: vals2 = obj._clean_defaults(defaults2) diff --git a/trytond/model/modelview.py b/trytond/model/modelview.py index ab4c8c0ce0..e35b4bbc16 100644 --- a/trytond/model/modelview.py +++ b/trytond/model/modelview.py @@ -293,9 +293,11 @@ class ModelView(Model): attrs = {} try: if element.get(attr) in self._columns: - relation = self._columns[element.get(attr)]._obj + relation = self._columns[element.get( + attr)].model_name else: - relation = self._inherit_fields[element.get(attr)][2]._obj + relation = self._inherit_fields[element.get( + attr)][2].model_name except: relation = False if relation and element.tag == 'field': diff --git a/trytond/osv/fields.py b/trytond/osv/fields.py index a3a1dad4c2..b64d664c8c 100644 --- a/trytond/osv/fields.py +++ b/trytond/osv/fields.py @@ -1,687 +1,3 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. -""" - Fields: - - simple - - relations (one2many, many2one, many2many) - - function - - Fields Attributes: - _classic_read: is a classic sql fields - _type : field type - readonly - required - size - - Relationals fields - - Values: ('create', { fields }) create - ('write', ids, { fields }) modification - ('delete', ids) remove (delete) - ('unlink', ids) unlink one (target id or target of relation) - ('add', ids) link - ('unlink_all') unlink all - ('set', ids) set a list of links -""" - -import psycopg2 -import warnings -import __builtin__ -import sha -import inspect - -def _symbol_f(symb): - if symb is None or symb == False: - return None - elif isinstance(symb, str): - return unicode(symb, 'utf-8') - elif isinstance(symb, unicode): - return symb - return unicode(symb) - -class Column(object): - _classic_read = True - _classic_write = True - _properties = False - _type = 'unknown' - _obj = None - _symbol_c = '%s' - _symbol_f = _symbol_f - _symbol_set = (_symbol_c, _symbol_f) - - def __init__(self, string='unknown', required=False, readonly=False, - domain=None, context='', states=None, priority=0, - change_default=False, size=None, ondelete="SET NULL", - translate=False, select=0, on_change=None, on_change_with=None, - depends=None, **args): - self.states = states or {} - self.string = string - self.readonly = readonly - self.required = required - self.size = size - self.help = args.get('help', '') - self.priority = priority - self.change_default = change_default - self.ondelete = ondelete - self.translate = translate - self._domain = domain or [] - self._context = context - self.depends = depends or [] - if select not in (0, 1, 2): - raise Exception('Error', 'Select must be one of 0, 1, 2') - self.select = select - self.on_change = on_change - self.on_change_with = on_change_with - self.order_field = None - for i in args: - if args[i]: - setattr(self, i, args[i]) - - def sql_type(self): - raise Exception('undefined sql_type method!') - - -class Boolean(Column): - _type = 'boolean' - _symbol_c = '%s' - _symbol_f = lambda x: x and 'True' or 'False' - _symbol_set = (_symbol_c, _symbol_f) - - def sql_type(self): - return ('bool', 'bool') - - -class Integer(Column): - _type = 'integer' - _symbol_c = '%s' - _symbol_f = lambda x: int(x or 0) - _symbol_set = (_symbol_c, _symbol_f) - - def sql_type(self): - return ('int4', 'int4') - - -class BigInteger(Integer): - _type = 'biginteger' - - def sql_type(self): - return ('int8', 'int8') - - -class Reference(Column): - _type = 'reference' - _classic_read = False - - def __init__(self, string, selection, size=None, **args): - Column.__init__(self, string=string, size=size, selection=selection, - **args) - - def get(self, cursor, obj, ids, name, user=None, offset=0, context=None, - values=None): - if values is None: - values = {} - res = {} - for i in values: - res[i['id']] = i[name] - for i in ids: - if not (i in res): - res[i] = False - continue - if not res[i]: - continue - ref_model, ref_id = res[i].split(',', 1) - if not ref_model: - continue - ref_obj = obj.pool.get(ref_model) - if not ref_obj: - continue - try: - ref_id = eval(ref_id) - except: - pass - try: - ref_id = int(ref_id) - except: - continue - if ref_id \ - and not ref_obj.search(cursor, user, [ - ('id', '=', ref_id), - ], context=context): - 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 + '")' - return res - - def sql_type(self): - if self.size: - return ('varchar', 'varchar(%d)' % (self.size,)) - else: - return ('varchar', 'varchar') - - -class Char(Column): - _type = 'char' - - def __init__(self, string, size=None, **args): - Column.__init__(self, string=string, size=size, **args) - - def sql_type(self): - if self.size: - return ('varchar', 'varchar(%d)' % (self.size,)) - else: - return ('varchar', 'varchar') - - -class Sha(Column): - _type = 'sha' - - def __init__(self, string, **args): - Column.__init__(self, string=string, size=40, **args) - self._symbol_f = lambda x: x and sha.new(x).hexdigest() or '' - self._symbol_set = (self._symbol_c, self._symbol_f) - - def sql_type(self): - return ('varchar', 'varchar(40)') - - -class Text(Column): - _type = 'text' - - def sql_type(self): - return ('text', 'text') - - -class Float(Column): - _type = 'float' - _symbol_c = '%s' - _symbol_f = lambda x: __builtin__.float(x or 0.0) - _symbol_set = (_symbol_c, _symbol_f) - - def __init__(self, string='unknown', digits=None, **args): - Column.__init__(self, string=string, **args) - self.digits = digits - - def sql_type(self): - return ('float8', 'float8') - - -class Numeric(Float): - _type = 'numeric' - - def sql_type(self): - return ('numeric', 'numeric') - - -class Date(Column): - _type = 'date' - - def sql_type(self): - return ('date', 'date') - - -class DateTime(Column): - _type = 'datetime' - - def sql_type(self): - return ('timestamp', 'timestamp(0)') - - -class Time(Column): - _type = 'time' - - def sql_type(self): - return ('time', 'time') - - -class Binary(Column): - _type = 'binary' - _symbol_c = '%s' - _symbol_f = lambda symb: symb and psycopg2.Binary(symb) or None - _symbol_set = (_symbol_c, _symbol_f) - - def sql_type(self): - return ('bytea', 'bytea') - - -class Selection(Column): - _type = 'selection' - - def __init__(self, selections, string='unknown', sort=True, translate=True, - **args): - """ - selections is a list of (key, string) - or the name of the object function that return the list - """ - self.sort = sort - self.translate_selection = translate - Column.__init__(self, string=string, selection=selections, **args) - - def sql_type(self): - if self.size: - return ('varchar', 'varchar(%d)' % (self.size,)) - else: - return ('varchar', 'varchar') - - -class Many2One(Column): - _classic_read = False - _classic_write = True - _type = 'many2one' - _symbol_c = '%s' - _symbol_f = lambda x: x and int(x) or None - _symbol_set = (_symbol_c, _symbol_f) - - def __init__(self, obj, string='unknown', left=None, right=None, **args): - Column.__init__(self, string=string, **args) - self._obj = obj - self.left = left - self.right = right - - def get(self, cursor, obj, ids, name, user=None, offset=0, context=None, - values=None): - if values is None: - values = {} - res = {} - for i in values: - res[i['id']] = i[name] - for i in ids: - res.setdefault(i, '') - try: - obj = obj.pool.get(self._obj) - except KeyError: - return res - obj_names = {} - for obj_id, name in obj.name_get(cursor, user, - [isinstance(x, (list, tuple)) and x[0] or x - for x in res.values() if x], - context=context): - obj_names[obj_id] = name - - for i in res.keys(): - if res[i] and res[i] in obj_names: - res[i] = (res[i], obj_names[res[i]]) - else: - res[i] = False - return res - - def set(self, cursor, obj_src, obj_id, field, values, user=None, - context=None): - obj = obj_src.pool.get(self._obj) - table = obj_src.pool.get(self._obj)._table - if type(values) == type([]): - for act in values: - if act[0] == 'create': - id_new = obj.create(cursor, user, act[2], context=context) - cursor.execute('UPDATE "' + obj_src._table + '" ' \ - 'SET "' + field + '" = %s ' \ - 'WHERE id = %s', (id_new, obj_id)) - elif act[0] == 'write': - obj.write(cursor, act[1], act[2], context=context) - elif act[0] == 'delete': - obj.delete(cursor, user, act[1], context=context) - cursor.execute('DELETE FROM "' + table + '" ' \ - 'WHERE id = %s', (act[1],)) - elif act[0] == 'unlink' or act[0] == 'unlink_all': - cursor.execute('UPDATE "' + obj_src._table + '" ' \ - 'SET "' + field + '" = NULL ' \ - 'WHERE id = %s', (obj_id,)) - elif act[0] == 'add': - cursor.execute('UPDATE "' + obj_src._table + '" ' \ - 'SET "' + field + '" = %s ' \ - 'WHERE id = %s', (act[1], obj_id)) - else: - if values: - cursor.execute('UPDATE "' + obj_src._table + '" ' \ - 'SET "' + field + '" = %s ' \ - 'WHERE id = %s', (values, obj_id)) - else: - cursor.execute('UPDATE "' + obj_src._table + '" ' \ - 'SET "' + field + '" = NULL ' \ - 'WHERE id = %s', (obj_id,)) - - def sql_type(self): - return ('int4', 'int4') - - -class One2Many(Column): - _classic_read = False - _classic_write = False - _type = 'one2many' - - def __init__(self, obj, field, string='unknown', limit=None, order=None, - **args): - Column.__init__(self, string=string, **args) - self._obj = obj - self._field = field - self._limit = limit - self._order = order - #one2many can't be used as condition for defaults - assert(self.change_default != True) - - def get(self, cursor, obj, ids, name, user=None, offset=0, context=None, - values=None): - res = {} - for i in ids: - res[i] = [] - ids2 = [] - for i in range(0, len(ids), cursor.IN_MAX): - sub_ids = ids[i:i + cursor.IN_MAX] - ids2 += obj.pool.get(self._obj).search(cursor, user, - [(self._field, 'in', sub_ids)], order=self._order, - context=context) - if self._limit and len(ids2) > offset + self._limit: - break - if offset: - ids2 = ids2[offset:] - if self._limit: - ids2 = ids2[:self._limit] - for i in obj.pool.get(self._obj).read(cursor, user, ids2, - [self._field], context=context): - res[i[self._field][0]].append( i['id'] ) - return res - - def set(self, cursor, obj, obj_id, field, values, user=None, context=None): - if not values: - return - _table = obj.pool.get(self._obj)._table - obj = obj.pool.get(self._obj) - for act in values: - if act[0] == 'create': - act[1][self._field] = obj_id - obj.create(cursor, user, act[1], context=context) - elif act[0] == 'write': - act[2][self._field] = obj_id - obj.write(cursor, user, act[1] , act[2], context=context) - elif act[0] == 'delete': - obj.delete(cursor, user, act[1], context=context) - elif act[0] == 'unlink': - if isinstance(act[1], (int, long)): - ids = [act[1]] - else: - ids = list(act[1]) - if not ids: - continue - ids = obj.search(cursor, user, [ - (self._field, '=', obj_id), - ('id', 'in', ids), - ], context=context) - obj.write(cursor, user, ids, { - self._field: False, - }, context=context) - elif act[0] == 'add': - if isinstance(act[1], (int, long)): - ids = [act[1]] - else: - ids = list(act[1]) - if not ids: - continue - ids = obj.write(cursor, user, ids, { - self._field: obj_id, - }, context=context) - elif act[0] == 'unlink_all': - ids = obj.search(cursor, user, [ - (self._field, '=', obj_id), - ], context=context) - obj.write(cursor, user, ids, { - self._field: False, - }, context=context) - elif act[0] == 'set': - if not act[1]: - ids = [0] - else: - ids = list(act[1]) - ids2 = obj.search(cursor, user, [ - (self._field, '=', obj_id), - ('id', 'not in', ids), - ], context=context) - obj.write(cursor, user, ids2, { - self._field: False, - }, context=context) - if act[1]: - obj.write(cursor, user, ids, { - self._field: obj_id, - }, context=context) - else: - raise Exception('Bad arguments') - - -class Many2Many(Column): - _classic_read = False - _classic_write = False - _type = 'many2many' - - def __init__(self, obj, rel, origin, target, string='unknown', limit=None, - order=None, ondelete_origin='CASCADE', ondelete_target='RESTRICT', - **args): - Column.__init__(self, string=string, **args) - self._obj = obj - self._rel = rel - self.origin = origin - self.ondelete_origin = ondelete_origin - self.target = target - self.ondelete_target = ondelete_target - self._limit = limit - self._order = order - - def get(self, cursor, obj, ids, name, user=None, offset=0, context=None, - values=None): - if values is None: - values = {} - res = {} - if not ids: - return res - for i in ids: - res[i] = [] - ids_s = ','.join([str(x) for x in ids]) - limit_str = self._limit is not None and ' limit %s' % self._limit or '' - obj = obj.pool.get(self._obj) - - domain1, domain2 = obj.pool.get('ir.rule').domain_get(cursor, - user, obj._name, context=context) - if domain1: - domain1 = ' and '+domain1 - - #TODO fix order: can have many fields - cursor.execute('SELECT ' + self._rel + '.' + self.target + ', ' + \ - self._rel + '.' + self.origin + ' ' \ - 'FROM "' + self._rel + '" , "' + obj._table + '" ' \ - 'WHERE ' + \ - self._rel + '.' + self.origin + ' IN (' + ids_s + ') ' \ - 'AND ' + self._rel + '.' + self.target + ' = ' + \ - obj._table + '.id ' + domain1 + \ - limit_str + ' ORDER BY ' + \ - ','.join([obj._table + '.' + x[0] + ' ' + x[1] \ - for x in (self._order or obj._order)]) + \ - ' OFFSET %s', domain2 + [offset]) - for i in cursor.fetchall(): - res[i[1]].append(i[0]) - return res - - def set(self, cursor, obj, obj_id, name, values, user=None, context=None): - if not values: - return - obj = obj.pool.get(self._obj) - for act in values: - if act[0] == 'create': - idnew = obj.create(cursor, user, act[1], context=context) - cursor.execute('INSERT INTO "' + self._rel + '" ' \ - '(' + self.origin + ', ' + self.target + ') ' \ - 'VALUES (%s, %s)', (obj_id, idnew)) - elif act[0] == 'write': - obj.write(cursor, user, act[1] , act[2], context=context) - elif act[0] == 'delete': - obj.delete(cursor, user, act[1], context=context) - elif act[0] == 'unlink': - if isinstance(act[1], (int, long)): - ids = [act[1]] - else: - ids = list(act[1]) - if not ids: - continue - cursor.execute('DELETE FROM "' + self._rel + '" ' \ - 'WHERE "' + self.origin + '" = %s ' \ - 'AND "'+ self.target + '" IN (' \ - + ','.join(['%s' for x in ids]) + ')', - [obj_id] + ids) - elif act[0] == 'add': - if isinstance(act[1], (int, long)): - ids = [act[1]] - else: - ids = list(act[1]) - if not ids: - continue - cursor.execute('SELECT "' + self.target + '" ' \ - 'FROM "' + self._rel + '" ' \ - 'WHERE "' + self.origin + '" = %s ' \ - 'AND "' + self.target + '" IN (' + \ - ','.join(['%s' for x in ids]) + ')', - [obj_id] + ids) - existing_ids = [] - for row in cursor.fetchall(): - existing_ids.append(row[0]) - new_ids = [x for x in ids if x not in existing_ids] - for new_id in new_ids: - cursor.execute('INSERT INTO "' + self._rel + '" ' \ - '("' + self.origin + '", "' + self.target + '") ' \ - 'VALUES (%s, %s)', (obj_id, new_id)) - elif act[0] == 'unlink_all': - cursor.execute('UPDATE "' + self._rel + '" ' \ - 'SET "' + self.target + '" = NULL ' \ - 'WHERE "' + self.target + '" = %s', (obj_id,)) - elif act[0] == 'set': - if not act[1]: - ids = [] - else: - ids = list(act[1]) - domain1, domain2 = obj.pool.get('ir.rule').domain_get(cursor, - user, obj._name, context=context) - if domain1: - domain1 = ' AND ' + domain1 - cursor.execute('DELETE FROM "' + self._rel + '" ' \ - 'WHERE "' + self.origin + '" = %s ' \ - 'AND "' + self.target + '" IN (' \ - 'SELECT ' + self._rel + '.' + self.target + ' ' \ - 'FROM "' + self._rel + '", "' + obj._table + '" ' \ - 'WHERE ' + self._rel + '.' + self.origin + ' = %s ' \ - 'AND ' + self._rel + '.' + self.target + ' = ' + \ - obj._table + '.id ' + domain1 + ')', - [obj_id, obj_id] + domain2) - - for new_id in ids: - cursor.execute('INSERT INTO "' + self._rel + '" ' \ - '("' + self.origin + '", "' + self.target + '") ' \ - 'VALUES (%s, %s)', (obj_id, new_id)) - else: - raise Exception('Bad arguments') - - -class Function(Column): - _classic_read = False - _classic_write = False - _properties = True - - def __init__(self, fnct, arg=None, fnct_inv='', fnct_inv_arg=None, - type='float', fnct_search='', obj=None, **args): - Column.__init__(self, **args) - self._obj = obj - self._fnct = fnct - self._fnct_inv = fnct_inv - self._arg = arg - self._field = '' - self._obj = '' - if 'relation' in args: - self._obj = args['relation'] - self._fnct_inv_arg = fnct_inv_arg - if not fnct_inv: - self.readonly = 1 - self._type = type - self._fnct_search = fnct_search - self._symbol_c = _TYPE2CLASS[self._type]._symbol_c - self._symbol_f = _TYPE2CLASS[self._type]._symbol_f - self._symbol_set = (self._symbol_c, self._symbol_f) - - def search(self, cursor, user, obj, name, args, context=None): - if not self._fnct_search: - return [] - return getattr(obj, self._fnct_search)(cursor, user, name, args, - context=context) - - def get(self, cursor, obj, ids, name, user=None, offset=0, context=None, - values=None): - if isinstance(name, list): - names = name - # Test is the function works with a list of names - if 'names' in inspect.getargspec(getattr(obj, self._fnct))[0]: - return getattr(obj, self._fnct)(cursor, user, ids, names, - self._arg, context=context) - res = {} - for name in names: - res[name] = getattr(obj, self._fnct)(cursor, user, ids, name, - self._arg, context=context) - return res - else: - # Test is the function works with a list of names - if 'names' in inspect.getargspec(getattr(obj, self._fnct))[0]: - name = [name] - return getattr(obj, self._fnct)(cursor, user, ids, name, self._arg, - context=context) - - def set(self, cursor, obj, obj_id, name, value, user=None, context=None): - if self._fnct_inv: - getattr(obj, self._fnct_inv)(cursor, user, obj_id, name, value, - self._fnct_inv_arg, context=context) - - -class Property(Function): - - def _fnct_write(self, obj, cursor, user, obj_id, prop, id_val, val, - context=None): - property_obj = obj.pool.get('ir.property') - return property_obj.set(cursor, user, prop, obj._name, obj_id, - (id_val and val + ',' + str(id_val)) or False, context=context) - - def _fnct_read(self, obj, cursor, user, ids, prop, arg, context=None): - property_obj = obj.pool.get('ir.property') - res = property_obj.get(cursor, user, prop, obj._name, ids, - context=context) - - if self._obj: - obj = obj.pool.get(self._obj) - obj_names = {} - for obj_id, obj_name in obj.name_get(cursor, user, - [x for x in res.values() if x], context=context): - obj_names[obj_id] = obj_name - for i in ids: - if res.get(i) and res[i] in obj_names: - res[i] = (res[i], obj_names[res[i]]) - else: - res[i] = False - return res - - def __init__(self, **args): - super(Property, self).__init__('', None, self._fnct_write, None, **args) - - def get(self, cursor, obj, ids, name, user=None, offset=0, context=None, - values=None): - return self._fnct_read(obj, cursor, user, ids, name, self._arg, - context=context) - - def set(self, cursor, obj, obj_id, name, value, user=None, context=None): - self._fnct_write(obj, cursor, user, obj_id, name, value, - self._obj, context=context) - -_TYPE2CLASS = { -} - -for klass in (Boolean, Integer, BigInteger, Reference, Char, Sha, Text, Float, - Numeric, Date, DateTime, Time, Binary, Selection, Many2One, One2Many, - Many2Many): - _TYPE2CLASS[klass._type] = klass +from trytond.model.fields import * diff --git a/trytond/sql_db.py b/trytond/sql_db.py index 4671abdf45..bd78b67274 100644 --- a/trytond/sql_db.py +++ b/trytond/sql_db.py @@ -12,6 +12,7 @@ import version from config import CONFIG import logging from trytond.security import Session +from trytond.backend.postgresql import * RE_FROM = re.compile('.* from "?([a-zA-Z_0-9]+)"?.*$') RE_INTO = re.compile('.* into "?([a-zA-Z_0-9]+)"?.*$') @@ -410,9 +411,11 @@ class table_handler: rtable = table_handler( self.cursor, relation_table, object_name=None, module_name= self.module_name) - from osv.fields import Integer - rtable.add_raw_column(rtable_from, ('int4', 'int4'), Integer._symbol_set) - rtable.add_raw_column(rtable_to, ('int4', 'int4'), Integer._symbol_set) + integer = FIELDS['integer'] + rtable.add_raw_column(rtable_from, integer.sql_type(None), + (integer.symbol_c, integer.symbol_f)) + rtable.add_raw_column(rtable_to, integer.sql_type(None), + (integer.symbol_c, integer.symbol_f)) rtable.add_fk(rtable_from, self.table_name, on_delete=on_delete_from) rtable.add_fk(rtable_to, other_table, on_delete=on_delete_to) rtable.not_null_action(rtable_from) diff --git a/trytond/tools/convert.py b/trytond/tools/convert.py index 32ced22545..5818064671 100644 --- a/trytond/tools/convert.py +++ b/trytond/tools/convert.py @@ -5,7 +5,6 @@ try: import cStringIO as StringIO except ImportError: import StringIO -from trytond.osv.fields import Integer import os.path from trytond.netsvc import LocalService from trytond.config import CONFIG @@ -194,7 +193,7 @@ class RecordTagHandler: eval_attr = attributes.get('eval', '') if search_attr: - search_model = self.model._columns[field_name]._obj + search_model = self.model._columns[field_name].model_name f_obj = self.mh.pool.get(search_model) answer = f_obj.browse( self.mh.cursor, self.mh.user,