Add model.fields

Use better naming of field attributes
This commit is contained in:
Cédric Krier committed 2009-02-03 18:58:25 +01:00
1 parent 6f9b7ac07a
commit e28edc4d99
29 files changed
+958 -855

No files matched your search

+4
View File
@@ -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 *
+4
View File
@@ -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 *
+193
View File
@@ -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,
}
+58 -63
View File
@@ -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)
+19
View File
@@ -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 *
+18
View File
@@ -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
+8
View File
@@ -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'
+12
View File
@@ -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
+16
View File
@@ -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'
+26
View File
@@ -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 ''
+12
View File
@@ -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
+59
View File
@@ -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)
+12
View File
@@ -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'
+128
View File
@@ -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')
+42
View File
@@ -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
+8
View File
@@ -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'
+92
View File
@@ -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')
+36
View File
@@ -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)
+59
View File
@@ -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
+14
View File
@@ -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
+8
View File
@@ -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'
+8
View File
@@ -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'
+21 -16
View File
@@ -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
+82 -74
View File
@@ -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
+7 -10
View File
@@ -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)
+4 -2
View File
@@ -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':
+1 -685
View File
@@ -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 *
+6 -3
View File
@@ -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)
+1 -2
View File
@@ -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,