Files
tryton/trytond/osv/orm.py
T

2528 lines
115 KiB
Python

# -*- coding: utf-8 -*-
from xml import dom
from xml.dom import minidom
from xml import xpath
from trytond.netsvc import Logger, LOG_ERROR, LOG_WARNING, LocalService
import fields
from trytond.tools import Cache
import md5
import time
import traceback
ID_MAX = 1000
def intersect(i, j):
return [x for x in j if x in i]
def exclude(i, j):
return [x for x in i if x not in j]
class ExceptORM(Exception):
def __init__(self, name, value):
Exception.__init__(self)
self.name = name
self.value = value
self.args = (name, value)
except_orm = ExceptORM
# TODO: execute an object method on BrowseRecordList
class BrowseRecordList(list):
def __init__(self, lst, context=None):
super(BrowseRecordList, self).__init__(lst)
self.context = context
browse_record_list = BrowseRecordList
class BrowseRecord(object):
def __init__(self, cursor, user, object_id, table, cache, context=None,
list_class=None):
'''
table : the object (inherited from orm)
context : a dictionnary with an optionnal context
'''
self._list_class = list_class or BrowseRecordList
self._cursor = cursor
self._user = user
self._id = object_id
self._table = table
self._table_name = self._table._name
self._context = context
cache.setdefault(table._name, {})
self._data = cache[table._name]
if not object_id in self._data:
self._data[object_id] = {'id': object_id}
self._cache = cache
def __getitem__(self, name):
if name == 'id':
return self._id
if not self._data[self._id].has_key(name) and self._id:
# 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)
else:
raise ExceptORM('Error', 'Programming error: field "%s" ' \
'does not exist in object "%s"!' \
% (name, self._table._name))
# if the field is a classic one or a many2one,
# we'll fetch all classic and many2one fields
if col._classic_write:
# 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]
# gen the list of inherited fields
inherits = [(x[0], x[1][2]) for x in \
self._table._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]
# otherwise we fetch only that field
else:
ffields = [(name, col)]
ids = [x for x in self._data.keys() \
if not self._data[x].has_key(name) and x]
# read the data
datas = self._table.read(self._cursor, self._user, ids,
[x[0] for x in ffields], context=self._context,
load="_classic_write")
# create browse records for 'remote' objects
for data in datas:
for i, j in ffields:
obj = self._table.pool.get(j._obj)
if not obj:
continue
if j._type in ('many2one', 'one2one'):
if not j._classic_write and data[i]:
ids2 = data[i][0]
else:
ids2 = data[i]
data[i] = BrowseRecord(self._cursor, self._user,
ids2, obj, self._cache,
context=self._context,
list_class=self._list_class)
elif j._type in ('one2many', 'many2many') and len(data[i]):
data[i] = self._list_class([BrowseRecord(self._cursor,
self._user, x, obj,
self._cache, context=self._context,
list_class=self._list_class) for x in data[i]],
self._context)
self._data[data['id']].update(data)
return self._data[self._id][name]
def __getattr__(self, name):
# TODO raise an AttributeError exception
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)
def __hasattr__(self, name):
return name in self
def __int__(self):
return self._id
def __str__(self):
return "BrowseRecord(%s, %d)" % (self._table_name, self._id)
def __eq__(self, other):
return (self._table_name, self._id) == (other._table_name, other._id)
def __ne__(self, other):
return (self._table_name, self._id) != (other._table_name, other._id)
# we need to define __unicode__ even though we've already defined __str__
# because we have overridden __getattr__
def __unicode__(self):
return unicode(str(self))
def __hash__(self):
return hash((self._table_name, self._id))
def __nonzero__(self):
return bool(self._id)
__repr__ = __str__
browse_record = BrowseRecord
class ORM(object):
"""
Object relationnal mapping to postgresql module
. Hierarchical structure
. Constraints consistency, validations
. Object meta Data depends on its status
. Optimised processing by complex query (multiple actions at once)
. Default fields value
. Permissions optimisation
. Persistant object: DB postgresql
. Datas conversions
. Multi-level caching system
. 2 different inheritancies
. Fields:
- classicals (varchar, integer, boolean, ...)
- relations (one2many, many2one, many2many)
- functions
"""
_log_access = True
_table = None
_table_query = ''
_name = None
_rec_name = 'name'
_parent_name = 'parent'
_date_name = 'date'
_order = None
_inherits = {} #XXX remove from class instance
_sequence = None
_description = ''
_auto = True
_obj = None
_sql = ''
pool = None
__columns = None
__defaults = None
def _getcolumns(self):
if self.__columns:
return self.__columns
res = {}
for attr in dir(self):
if attr in ('_columns', '_defaults'):
continue
if isinstance(getattr(self, attr), fields.Column):
res[attr] = getattr(self, attr)
self.__columns = res
return res
_columns = property(fget=_getcolumns)
def _getdefaults(self):
if self.__defaults:
return self.__defaults
res = {}
columns = self._columns.keys()
columns += self._inherit_fields.keys()
for column in columns:
if getattr(self, 'default_' + column, False):
res[column] = getattr(self, 'default_' + column)
self.__defaults = res
return res
_defaults = property(fget=_getdefaults)
def _field_create(self, cursor, module_name):
cursor.execute("SELECT id FROM ir_model WHERE model = %s",
(self._name,))
if not cursor.rowcount:
# reference model in order to have a description
# of its fonctionnality in custom_report
cursor.execute("INSERT INTO ir_model " \
"(model, name, info) VALUES (%s, %s, %s)",
(self._name, self._description, self.__doc__))
cursor.execute("SELECT id FROM ir_model WHERE model = %s",
(self._name,))
(model_id,) = cursor.fetchone()
cursor.execute('SELECT f.id AS id, f.name AS name, ' \
'f.field_description AS field_description, ' \
'f.ttype AS ttype, f.relation AS relation, ' \
'f.group_name AS group_name, f.view_load AS view_load ' \
'FROM ir_model_field AS f, ir_model AS m ' \
'WHERE f.model = m.id ' \
'AND m.model = %s ' \
'AND f.name in ' \
'(' + ','.join(['%s' for x in self._columns]) + ')',
(self._name,) + tuple(self._columns))
columns = {}
for column in cursor.dictfetchall():
columns[column['name']] = column
cursor.execute('SELECT id, name, src, type FROM ir_translation ' \
'WHERE lang = %s ' \
'AND type IN (%s, %s, %s) ' \
'AND name IN ' \
'(' + ','.join(['%s' for x in self._columns]) + ')',
('en_US', 'field', 'help', 'selection') + \
tuple([self._name + ',' + x \
for x in self._columns]))
trans_columns = {}
trans_help = {}
trans_selection = {}
for trans in cursor.dictfetchall():
if trans['type'] == 'field':
trans_columns[trans['name']] = trans
elif trans['type'] == 'help':
trans_help[trans['name']] = trans
elif trans['type'] == 'selection':
trans_selection.setdefault(trans['name'], {})
trans_selection[trans['name']][trans['src']] = trans
for k in self._columns:
field = self._columns[k]
if k not in columns:
cursor.execute("INSERT INTO ir_model_field " \
"(model, name, field_description, ttype, " \
"relation, group_name, view_load, help) " \
"VALUES (%s, %s, %s, %s, %s, %s, %s, %s)",
(model_id, k, field.string, field._type,
field._obj or '', field.group_name or '',
(field.view_load and 'True') or 'False',
field.help))
elif columns[k]['field_description'] != field.string \
or columns[k]['ttype'] != field._type \
or columns[k]['relation'] != (field._obj or '') \
or columns[k]['group_name'] != (field.group_name or '') \
or columns[k]['view_load'] != \
((field.view_load and 'True') or 'False') \
or columns[k]['help'] != field.help:
cursor.execute('UPDATE ir_model_field ' \
'SET field_description = %s, ' \
'ttype = %s, ' \
'relation = %s, ' \
'group_name = %s, ' \
'view_load = %s, ' \
'help = %s ' \
'WHERE id = %s ',
(field.string, field._type, field._obj or '',
field.group_name or '',
(field.view_load and 'True') or 'False',
field.help, columns[k]['id']))
trans_name = self._name + ',' + k
if trans_name not in trans_columns:
cursor.execute('INSERT INTO ir_translation ' \
'(name, lang, type, src, value, module) ' \
'VALUES (%s, %s, %s, %s, %s, %s)',
(trans_name, 'en_US', 'field',
field.string, '', module_name))
elif trans_columns[trans_name]['src'] != field.string:
cursor.execute('UPDATE ir_translation ' \
'SET src = %s ' \
'WHERE id = %s ',
(field.string, trans_columns[trans_name]['id']))
if trans_name not in trans_help:
if field.help:
cursor.execute('INSERT INTO ir_translation ' \
'(name, lang, type, src, value, module) ' \
'VALUES (%s, %s, %s, %s, %s, %s)',
(trans_name, 'en_US', 'help',
field.help, '', module_name))
elif trans_help[trans_name]['src'] != field.help:
cursor.execute('UPDATE ir_translation ' \
'SET src = %s ' \
'WHERE id = %s ',
(field.help, trans_help[trans_name]['id']))
if hasattr(field, 'selection') \
and isinstance(field.selection, (tuple, list)):
for (key, val) in field.selection:
if trans_name not in trans_selection \
or val not in trans_selection[trans_name]:
cursor.execute('INSERT INTO ir_translation ' \
'(name, lang, type, src, value, ' \
'module) ' \
'VALUES (%s, %s, %s, %s, %s, %s)',
(trans_name, 'en_US', 'selection', val, '',
module_name))
def auto_init(self, cursor, module_name):
self.init(cursor, module_name)
self._auto_init(cursor, module_name)
def init(self, cursor, module_name):
pass
def _auto_init(self, cursor, module_name):
logger = Logger()
create = False
self._field_create(cursor, module_name)
if self._auto and not self._table_query:
cursor.execute("SELECT relname FROM pg_class " \
"WHERE relkind in ('r', 'v') AND relname = %s",
(self._table,))
if not cursor.rowcount:
cursor.execute("CREATE TABLE \"%s\" " \
"(id SERIAL NOT NULL, " \
"PRIMARY KEY(id)) WITH OIDS" % self._table)
create = True
if self._log_access:
logs = {
'create_uid': 'INTEGER REFERENCES res_user ' \
'ON DELETE SET NULL',
'create_date': 'TIMESTAMP',
'write_uid': 'INTEGER REFERENCES res_user ' \
'ON DELETE SET NULL',
'write_date': 'TIMESTAMP'
}
for k in logs:
cursor.execute("SELECT c.relname " \
"FROM pg_class c, pg_attribute a " \
"WHERE c.relname = %s " \
"AND a.attname = %s " \
"AND c.oid = a.attrelid",
(self._table, k))
if not cursor.rowcount:
cursor.execute("ALTER TABLE \"%s\" " \
"ADD COLUMN \"%s\" %s" %
(self._table, k, logs[k]))
# iterate on the database columns to drop the NOT NULL constraints
# of fields which were required but have been removed
cursor.execute(
"SELECT a.attname, a.attnotnull "\
"FROM pg_class c, pg_attribute a "\
"WHERE c.oid=a.attrelid AND c.relname='%s'" % self._table)
db_columns = cursor.dictfetchall()
for column in db_columns:
if column['attname'] not in (
'id',
'oid',
'tableoid',
'ctid',
'xmin',
'xmax',
'cmin',
'cmax',
):
if column['attnotnull'] \
and (column['attname'] not in self._columns):
cursor.execute("ALTER TABLE \"%s\" " \
"ALTER COLUMN \"%s\" DROP NOT NULL" % \
(self._table, column['attname']))
# iterate on the "object columns"
for k in self._columns:
if k in (
'id',
'write_uid',
'write_date',
'create_uid',
'create_date',
):
continue
field = self._columns[k]
if isinstance(field, fields.One2Many):
cursor.execute("SELECT relname FROM pg_class " \
"WHERE relkind = 'r' AND relname = %s",
(field._obj,))
if cursor.fetchone():
cursor.execute("SELECT count(*) as c " \
"FROM pg_class c, pg_attribute a " \
"WHERE c.relname = %s " \
"AND a.attname = %s " \
"AND c.oid = a.attrelid",
(field._obj, field._field))
(res,) = cursor.fetchone()
if not res:
cursor.execute("ALTER TABLE \"%s\" " \
"ADD FOREIGN KEY (%s) " \
"REFERENCES \"%s\" ON DELETE SET NULL" % \
(self._obj, field._field, field._table))
elif isinstance(field, fields.Many2Many):
cursor.execute("SELECT relname FROM pg_class " \
"WHERE relkind in ('r','v') AND relname=%s",
(field._rel,))
if not cursor.dictfetchall():
#FIXME: Remove this try/except
try:
ref = self.pool.get(field._obj)._table
except AttributeError:
ref = field._obj.replace('.','_')
cursor.execute("CREATE TABLE \"%s\" " \
"(\"%s\" INTEGER NOT NULL REFERENCES \"%s\" " \
"ON DELETE CASCADE, " \
"\"%s\" INTEGER NOT NULL REFERENCES \"%s\" " \
"ON DELETE CASCADE) WITH OIDS" % \
(field._rel, field._id1, self._table,
field._id2, ref))
cursor.execute("CREATE INDEX \"%s_%s_index\" " \
"ON \"%s\" (\"%s\")" % \
(field._rel, field._id1, field._rel,
field._id1))
cursor.execute("CREATE INDEX \"%s_%s_index\" " \
"ON \"%s\" (\"%s\")" % \
(field._rel, field._id2, field._rel,
field._id2))
else:
cursor.execute("SELECT c.relname, a.attname, a.attlen, " \
"a.atttypmod, a.attnotnull, a.atthasdef, " \
"t.typname, " \
"CASE WHEN a.attlen = -1 " \
"THEN a.atttypmod-4 " \
"ELSE a.attlen END as size " \
"FROM pg_class c, pg_attribute a, pg_type t " \
"WHERE c.relname = %s " \
"AND a.attname = %s " \
"AND c.oid = a.attrelid " \
"AND a.atttypid = t.oid",
(self._table, k))
res = cursor.dictfetchall()
if not res:
if not isinstance(field, fields.Function):
# add the missing field
cursor.execute("ALTER TABLE \"%s\" " \
"ADD COLUMN \"%s\" %s" % \
(self._table, k, field.sql_type()[1]))
# initialize it
if not create and k in self._defaults:
default = self._defaults[k](cursor, 1, {})
if not default:
cursor.execute("UPDATE \"%s\" " \
"SET \"%s\" = NULL" % \
(self._table, k))
else:
if isinstance(field, fields.Many2One) \
and isinstance(default, (list, tuple)):
default = default[0]
cursor.execute("UPDATE \"%s\" " \
"SET \"%s\" = '%s'" % \
(self._table, k, default))
# and add constraints if needed
if isinstance(field, fields.Many2One):
# res.user and res.group are not present when ir initialize
if field._obj == 'res.user':
ref = 'res_user'
elif field._obj == 'res.group':
ref = 'res_group'
else:
ref = self.pool.get(field._obj)._table
cursor.execute("ALTER TABLE \"%s\" " \
"ADD FOREIGN KEY (\"%s\") " \
"REFERENCES \"%s\" " \
"ON DELETE %s" % \
(self._table, k, ref,
field.ondelete))
if field.select:
cursor.execute("CREATE INDEX \"%s_%s_index\" " \
"ON \"%s\" (\"%s\")" % \
(self._table, k, self._table, k))
if field.required:
cursor.execute('SELECT id FROM "%s" ' \
'WHERE "%s" IS NULL' % \
(self._table, k))
if not cursor.rowcount:
cursor.execute("ALTER TABLE \"%s\" " \
"ALTER COLUMN \"%s\" " \
"SET NOT NULL" % \
(self._table, k))
else:
logger.notify_channel('init',
LOG_WARNING,
'Unable to set column %s ' \
'of table %s not null !\n'\
'Try to re-run: ' \
'trytond.py --update=module\n' \
'If it doesn\'t work, update records and execute manually:\n' \
'ALTER TABLE %s ALTER COLUMN %s SET NOT NULL' % \
(k, self._table, self._table, k))
elif len(res)==1:
f_pg_def = res[0]
f_pg_type = f_pg_def['typname']
f_pg_size = f_pg_def['size']
f_pg_notnull = f_pg_def['attnotnull']
if isinstance(field, fields.Function):
logger.notify_channel('init', LOG_WARNING,
'column %s (%s) in table %s was converted '\
'to a function !\n' \
'You should remove this column from your database.' % \
(k, field.string, self._table))
f_obj_type = None
else:
f_obj_type = field.sql_type() \
and field.sql_type()[0]
if f_obj_type:
if f_pg_type != f_obj_type:
logger.notify_channel('init',
LOG_WARNING,
"column '%s' in table '%s' must " \
"change type %s -> %s!" % \
(k, self._table, f_pg_type, f_obj_type,))
if f_pg_type == 'varchar' \
and field._type == 'char' \
and (f_pg_size != field.size \
and not (f_pg_size == -5 and field.size == None)):
# columns with the name 'type' cannot be changed
# for an unknown reason?!
if k != 'type':
if field.size and f_pg_size > field.size:
logger.notify_channel('init',
LOG_WARNING,
"column '%s' in table '%s' " \
"has changed size (DB = %d, def = %d), strings will be truncated !" % \
(k, self._table, f_pg_size, field.size))
#TODO: check si y a des donnees qui vont poser probleme (select char_length())
#TODO: issue a log message even if f_pg_size < field.size
cursor.execute("ALTER TABLE \"%s\" " \
"RENAME COLUMN \"%s\" " \
"TO temp_change_size" % \
(self._table,k))
cursor.execute("ALTER TABLE \"%s\" " \
"ADD COLUMN \"%s\" %s" % \
(self._table, k, field.sql_type()[1]))
cursor.execute("UPDATE \"%s\" " \
"SET \"%s\" = temp_change_size::%s" % \
(self._table, k, field.sql_type()[1]))
cursor.execute("ALTER TABLE \"%s\" " \
"DROP COLUMN temp_change_size" % \
(self._table,))
# if the field is required
# and hasn't got a NOT NULL constraint
if field.required and f_pg_notnull == 0:
# set the field to the default value if any
if self._defaults.has_key(k):
default = self._defaults[k](cursor,
1, {})
if not (default is False):
cursor.execute("UPDATE \"%s\" " \
"SET \"%s\" = '%s' WHERE %s is NULL" % \
(self._table, k, default, k))
cursor.execute('SELECT "%s" FROM "%s" ' \
'WHERE "%s" IS NULL' % \
(k, self._table, k))
if not cursor.rowcount:
# add the NOT NULL constraint
cursor.execute("ALTER TABLE \"%s\" " \
"ALTER COLUMN \"%s\" SET NOT NULL" % \
(self._table, k))
else:
logger.notify_channel('init',
LOG_WARNING,
'unable to set ' \
'a NOT NULL constraint on column %s of the %s table !\n' \
'If you want to have it, you should update the records and execute manually:\n'\
'ALTER TABLE %s ALTER COLUMN %s SET NOT NULL' % \
(k, self._table, self._table, k))
elif not field.required and f_pg_notnull == 1:
cursor.execute("ALTER TABLE \"%s\" " \
"ALTER COLUMN \"%s\" DROP NOT NULL" % \
(self._table, k))
cursor.execute("SELECT indexname FROM pg_indexes " \
"WHERE indexname = '%s_%s_index' AND tablename = '%s'" % \
(self._table, k, self._table))
res = cursor.dictfetchall()
if not res and field.select:
cursor.execute("CREATE INDEX \"%s_%s_index\" " \
"ON \"%s\" (\"%s\")" % \
(self._table, k, self._table, k))
if res and not field.select:
cursor.execute("DROP INDEX \"%s_%s_index\"" % \
(self._table, k))
if isinstance(field, fields.Many2One):
ref_obj = self.pool.get(field._obj)
if ref_obj:
ref = ref_obj._table
else:
ref = field._obj.replace('.', '_')
if ref != 'ir_actions':
cursor.execute('SELECT confdeltype, conname ' \
'FROM pg_constraint as con, ' \
'pg_class as cl1, pg_class as cl2, ' \
'pg_attribute as att1, pg_attribute as att2 ' \
'WHERE con.conrelid = cl1.oid ' \
'AND cl1.relname = %s ' \
'AND con.confrelid = cl2.oid ' \
'AND cl2.relname = %s ' \
'AND array_lower(con.conkey, 1) = 1 ' \
'AND con.conkey[1] = att1.attnum ' \
'AND att1.attrelid = cl1.oid ' \
'AND att1.attname = %s ' \
'AND array_lower(con.confkey, 1) = 1 ' \
'AND con.confkey[1] = att2.attnum ' \
'AND att2.attrelid = cl2.oid ' \
'AND att2.attname = %s ' \
'AND con.contype = \'f\'',
(self._table, ref, k, 'id'))
res = cursor.dictfetchall()
if res:
confdeltype = {
'RESTRICT': 'r',
'NO ACTION': 'a',
'CASCADE': 'c',
'SET NULL': 'n',
'SET DEFAULT': 'd',
}
if res[0]['confdeltype'] != \
confdeltype.get(
field.ondelete.upper(),
'a'):
cursor.execute('ALTER TABLE "' + \
self._table + '" ' \
'DROP CONSTRAINT "' + \
res[0]['conname'] + '"')
cursor.execute('ALTER TABLE "' + \
self._table + '" ' \
'ADD FOREIGN KEY ' \
'("' + k + '") ' \
'REFERENCES "' + ref + \
'" ON DELETE ' + field.ondelete)
else:
# TODO add error message
logger.notify_channel('init', LOG_ERROR, '')
else:
cursor.execute("SELECT relname FROM pg_class " \
"WHERE relkind in ('r', 'v') AND relname = %s",
(self._table,))
create = not bool(cursor.fetchone())
for (key, con, msg) in self._sql_constraints:
cursor.execute("SELECT conname FROM pg_constraint " \
"WHERE conname = %s", ((self._table + '_' + key),))
if not cursor.dictfetchall():
try:
cursor.execute('ALTER TABLE \"%s\" ' \
'ADD CONSTRAINT \"%s_%s\" %s' % \
(self._table, self._table, key, con,))
except:
logger.notify_channel('init', LOG_WARNING,
'unable to add \'%s\' constraint on table %s !\n' \
'If you want to have it, you should update the records and execute manually:\n'\
'ALTER table %s ADD CONSTRAINT %s_%s %s' % \
(con, self._table, self._table, self._table,key, con,))
if create:
if hasattr(self, "_sql"):
for line in self._sql.split(';'):
line2 = line.replace('\n', '').strip()
if line2:
cursor.execute(line2)
def __init__(self):
self._rpc_allowed = [
'read',
'write',
'create',
'default_get',
'unlink',
'fields_get',
'fields_view_get',
'search',
'name_get',
'name_search',
'copy',
'import_data',
'export_data',
'search_count',
]
self._sql_constraints = []
self._constraints = []
self._inherit_fields = []
self._order = [('id', 'ASC')]
# reinit the cache on _columns and _defaults
self.__columns = None
self.__defaults = None
if not self._table:
self._table = self._name.replace('.', '_')
if not self._description:
self._description = self._name
self._inherits_reload()
if not self._sequence:
self._sequence = self._table+'_id_seq'
if self._log_access:
self.create_uid = fields.Many2One('res.user',
'Creation user', required=True, readonly=True)
self.create_date = fields.DateTime('Creation date',
required=True, readonly=True)
self.write_uid = fields.Many2One('res.user',
'Last modification by', readonly=True)
self.write_date = fields.DateTime(
'Last modification date', readonly=True)
self.id = fields.Integer('ID', readonly=True)
for name in self._columns:
if isinstance(self._columns[name], (fields.Selection, fields.Reference)) \
and not isinstance(self._columns[name].selection, (list, tuple)) \
and self._columns[name].selection not in self._rpc_allowed:
self._rpc_allowed.append(self._columns[name].selection)
if self._columns[name].on_change:
on_change = 'on_change_' + name
if on_change not in self._rpc_allowed:
self._rpc_allowed.append(on_change)
for k in self._defaults:
assert (k in self._columns) or (k in self._inherit_fields), \
'Default function defined in %s but field %s does not exist!' % \
(self._name, k,)
def _inherits_reload_src(self):
"Update objects that uses this one to update their _inherits fields"
for obj in self.pool.object_name_pool.values():
if self._name in obj._inherits:
obj._inherits_reload()
def _inherits_reload(self):
res = {}
for table in self._inherits:
res.update(self.pool.get(table)._inherit_fields)
for col in self.pool.get(table)._columns.keys():
res[col] = (table, self._inherits[table],
self.pool.get(table)._columns[col])
for col in self.pool.get(table)._inherit_fields.keys():
res[col] = (table, self._inherits[table],
self.pool.get(table)._inherit_fields[col][2])
self._inherit_fields = res
self._inherits_reload_src()
def default_create_uid(self, cursor, user, context=None):
return user
def default_create_date(self, cursor, user, context=None):
return time.strftime("%Y-%m-%d %H:%M:%S")
def browse(self, cursor, user, select, context=None, list_class=None):
list_class = list_class or BrowseRecordList
cache = {}
# need to accepts ints and longs because ids coming from a method
# launched by button in the interface have a type long...
if isinstance(select, (int, long)):
return BrowseRecord(cursor, user, select, self, cache,
context=context, list_class=list_class)
return list_class([BrowseRecord(cursor, user, x, self, cache,
context=context, list_class=list_class) for x in select],
context)
def __export_row(self, cursor, user, row, fields_names, context=None):
lines = []
data = ['' for x in range(len(fields_names))]
done = []
for fpos in range(len(fields_names)):
field = fields_names[fpos]
if field:
row2 = row
i = 0
while i < len(field):
row2 = row2[field[i]]
if not row2:
break
if isinstance(row2, (BrowseRecordList, list)):
first = True
fields2 = [(x[:i+1]==field[:i+1] and x[i+1:]) \
or [] for x in fields_names]
if fields2 in done:
break
done.append(fields2)
for row2 in row2:
lines2 = self.__export_row(cursor, user, row2,
fields2, context)
if first:
for fpos2 in range(len(fields_names)):
if lines2 and lines2[0][fpos2]:
data[fpos2] = lines2[0][fpos2]
lines += lines2[1:]
first = False
else:
lines += lines2
break
i += 1
if i == len(field):
data[fpos] = str(row2 or '')
return [data] + lines
def export_data(self, cursor, user, ids, fields_names, context=None):
fields_names = [x.split('/') for x in fields_names]
datas = []
for row in self.browse(cursor, user, ids, context):
datas += self.__export_row(cursor, user, row, fields_names, context)
return datas
# TODO: Send a request with the result and multi-thread !
def import_data(self, cursor, user, fields_names, datas, mode='init',
current_module=None, noupdate=False, context=None):
if context is None:
context = {}
fields_names = [x.split('/') for x in fields_names]
logger = Logger()
def process_liness(self, datas, prefix, fields_def, position=0):
line = datas[position]
row = {}
translate = {}
todo = []
warning = ''
data_id = False
# Import normal fields_names
for i in range(len(fields_names)):
if i >= len(line):
raise Exception, \
'Please check that all your lines have %d cols.' % \
(len(fields_names),)
field = fields_names[i]
if field == ["id"]:
data_id = line[i]
continue
if (len(field) == len(prefix) + 1) \
and field[len(prefix)].endswith(':id'):
res_id = False
if line[i]:
if fields_def[field[len(prefix)][:-3]]['type'] \
== 'many2many':
res_id = []
for word in line[i].split(','):
if '.' in word:
module, xml_id = word.rsplit('.', 1)
else:
module, xml_id = current_module, word
ir_model_data_obj = \
self.pool.get('ir.model.data')
new_id = ir_model_data_obj._get_id(cursor,
user, module, xml_id)
res_id2 = ir_model_data_obj.read(cursor, user,
[new_id], ['res_id'])[0]['res_id']
if res_id2:
res_id.append(res_id2)
if len(res_id):
res_id = [('set', res_id)]
else:
if '.' in line[i]:
module, xml_id = line[i].rsplit('.', 1)
else:
module, xml_id = current_module, line[i]
ir_model_data_obj = self.pool.get('ir.model.data')
new_id = ir_model_data_obj._get_id(cursor, user,
module, xml_id)
res_id = ir_model_data_obj.read(cursor, user,
[new_id], ['res_id'])[0]['res_id']
row[field[0][:-3]] = res_id or False
continue
if (len(field) == len(prefix)+1) and \
len(field[len(prefix)].split(':lang=')) == 2:
field, lang = field[len(prefix)].split(':lang=')
translate.setdefault(lang, {})[field]=line[i] or False
continue
if (len(field) == len(prefix)+1) and \
(prefix == field[0:len(prefix)]):
if fields_def[field[len(prefix)]]['type'] == 'integer':
res = line[i] and int(line[i])
elif fields_def[field[len(prefix)]]['type'] == 'float':
res = line[i] and float(line[i])
elif fields_def[field[len(prefix)]]['type'] == 'selection':
res = False
if isinstance(
fields_def[field[len(prefix)]]['selection'],
(tuple, list)):
sel = fields_def[field[len(prefix)]]['selection']
else:
sel = getattr(self, fields_def[field[len(prefix)]]\
['selection'])(cursor, user, context)
for key, val in sel:
if str(key) == line[i]:
res = key
if line[i] and not res:
logger.notify_channel("import", LOG_WARNING,
"key '%s' not found " \
"in selection field '%s'" % \
(line[i], field[len(prefix)]))
elif fields_def[field[len(prefix)]]['type'] == 'many2one':
res = False
if line[i]:
relation = \
fields_def[field[len(prefix)]]['relation']
res2 = self.pool.get(relation).name_search(cursor,
user, line[i], [], operator='=')
res = (res2 and res2[0][0]) or False
if not res:
warning += ('Relation not found: ' + line[i] + \
' on ' + relation + ' !\n')
logger.notify_channel("import",
LOG_WARNING,
'Relation not found: ' + line[i] + \
' on ' + relation + ' !\n')
elif fields_def[field[len(prefix)]]['type'] == 'many2many':
res = []
if line[i]:
relation = \
fields_def[field[len(prefix)]]['relation']
for word in line[i].split(','):
res2 = self.pool.get(relation).name_search(
cursor, user, word, [], operator='=')
res3 = (res2 and res2[0][0]) or False
if not res3:
warning += ('Relation not found: ' + \
line[i] + ' on '+relation + ' !\n')
logger.notify_channel("import",
LOG_WARNING,
'Relation not found: ' + line[i] + \
' on '+relation + ' !\n')
else:
res.append(res3)
if len(res):
res = [('set', res)]
else:
res = line[i] or False
row[field[len(prefix)]] = res
elif (prefix==field[0:len(prefix)]):
if field[0] not in todo:
todo.append(field[len(prefix)])
# Import one2many fields
nbrmax = 1
for field in todo:
newfd = self.pool.get(fields_def[field]['relation']).fields_get(
cursor, user, context=context)
res = process_liness(self, datas, prefix + [field], newfd,
position)
(newrow, max2, warning2, translate2, data_id2) = res
nbrmax = max(nbrmax, max2)
warning = warning + warning2
reduce(lambda x, y: x and y, newrow)
row[field] = (reduce(lambda x, y: x or y, newrow.values()) and \
[('create', newrow)]) or []
i = max2
while (position+i)<len(datas):
test = True
for j in range(len(fields_names)):
field2 = fields_names[j]
if (len(field2) <= (len(prefix)+1)) \
and datas[position+i][j]:
test = False
if not test:
break
(newrow, max2, warning2, translate2, data_id2) = \
process_liness(self, datas, prefix+[field], newfd,
position + i)
warning = warning + warning2
if reduce(lambda x, y: x or y, newrow.values()):
row[field].append(('create', newrow))
i += max2
nbrmax = max(nbrmax, i)
if len(prefix) == 0:
for i in range(max(nbrmax, 1)):
datas.pop(0)
result = (row, nbrmax, warning, translate, data_id)
return result
fields_def = self.fields_get(cursor, user, context=context)
done = 0
while len(datas):
res = {}
try:
(res, other, warning, translate, data_id) = \
process_liness(self, datas, [], fields_def)
if warning:
cursor.rollback()
return (-1, res, warning, '')
new_id = self.pool.get('ir.model.data')._update(cursor, user,
self._name, current_module, res, xml_id=data_id,
mode=mode, noupdate=noupdate)
for lang in translate:
context2 = context.copy()
context2['language'] = lang
self.write(cursor, user, [new_id], translate[lang],
context=context2)
except Exception, exp:
logger.notify_channel("import", LOG_ERROR, exp)
cursor.rollback()
return (-1, res, exp[0], warning)
done += 1
return (done, 0, 0, 0)
def read(self, cursor, user, ids, fields_names=None, context=None,
load='_classic_read'):
self.pool.get('ir.model.access').check(cursor, user, self._name, 'read')
if not fields_names:
fields_names = self._columns.keys() + \
exclude(self._inherit_fields.keys(), self._columns.keys())
select = ids
if isinstance(ids, (int, long)):
select = [ids]
result = self._read_flat(cursor, user, select, fields_names, context,
load)
for i in result:
for key, j in i.items():
if j == None:
i[key] = False
if isinstance(ids, (int, long)):
return result[0]
return result
def _read_flat(self, cursor, user, ids, fields_names, context=None,
load='_classic_read'):
if context is None:
context = {}
if not ids:
return []
if fields_names == None:
fields_names = self._columns.keys()
# construct a clause for the rules :
domain1, domain2 = self.pool.get('ir.rule').domain_get(cursor, user,
self._name)
# 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')] + \
self._inherits.values()
res = []
table_query = ''
if self._table_query:
table_query = '(' + self._table_query + ') AS '
if len(fields_pre) :
fields_pre2 = [(x in ('create_date', 'write_date')) \
and ('date_trunc(\'second\', ' + x + ') as ' + x) \
or '"' + x + '"' for x in fields_pre]
for i in range((len(ids) / ID_MAX) + ((len(ids) % ID_MAX) and 1 or 0)):
sub_ids = ids[ID_MAX * i:ID_MAX * (i + 1)]
if domain1:
cursor.execute(('SELECT ' + \
','.join(fields_pre2 + ['id']) + \
' FROM ' + table_query + '\"' + self._table +'\" ' \
'WHERE id IN ' \
'(' + ','.join([str(x) for x in sub_ids]) + ')'\
' AND ' + domain1 + ' ORDER BY ' + \
','.join([self._table + '.' + x[0] + ' ' + x[1] \
for x in self._order])),
domain2)
if not cursor.rowcount == len({}.fromkeys(sub_ids)):
raise ExceptORM('AccessError',
'You try to bypass an access rule ' \
'(Document type: %s).' % \
self._description)
else:
cursor.execute('SELECT ' + \
','.join(fields_pre2 + ['id']) + \
' FROM ' + table_query + '\"' + self._table + '\" ' \
'WHERE id IN ' \
'(' + ','.join([str(x) for x in sub_ids]) + ')'\
' ORDER BY ' + \
','.join([self._table + '.' + x[0] + ' ' + x[1] \
for x in self._order]))
res.extend(cursor.dictfetchall())
else:
res = [{'id': x} for x in ids]
for field in fields_pre:
if self._columns[field].translate:
ids = [x['id'] for x in res]
res_trans = self.pool.get('ir.translation')._get_ids(cursor,
self._name + ',' + field, 'model',
context.get('language', False) or 'en_US', ids)
for i in res:
i[field] = res_trans.get(i['id'], False) or i[field]
for table in self._inherits:
col = self._inherits[table]
cols = intersect(self._inherit_fields.keys(), fields_names)
cols = exclude(cols, self._columns.keys())
if not cols:
continue
res2 = self.pool.get(table).read(cursor, user,
[x[col] for x in res], cols, context, load)
res3 = {}
for i in res2:
res3[i['id']] = i
del i['id']
for record in res:
record.update(res3[record[col]])
if col not in fields_names:
del record[col]
# all fields which need to be post-processed
# by a simple function (symbol_get)
fields_post = [x for x in fields_names if x in self._columns \
and self._columns[x]._symbol_get]
if fields_post:
# maybe it would be faster to iterate on the fields_names then
# on res, so that we wouldn't need to get the _symbol_get
# in each occurence
for i in res:
for field in fields_post:
i[field] = self._columns[field]._symbol_get(i[field])
ids = [x['id'] for x in res]
# all non inherited fields for which the attribute
# whose name is in load is False
fields_post = [x for x in fields_names if x in self._columns \
and not getattr(self._columns[x], load)]
for field in fields_post:
# get the value of that field for all records/ids
res2 = self._columns[field].get(cursor, self, ids, field, user,
context=context, values=res)
for record in res:
record[field] = res2[record['id']]
return res
def _validate(self, cursor, user, ids):
field_error = []
field_err_str = []
for field in self._constraints:
if not getattr(self, field[0])(cursor, user, ids):
if len(field) > 1:
field_error += field[2]
field_err_str.append(field[1])
if len(field_err_str):
raise ExceptORM('ValidateError',
('\n'.join(field_err_str), ','.join(field_error)))
def default_get(self, cursor, user, fields_names, context=None):
value = {}
# get the default values for the inherited fields
for i in self._inherits.keys():
value.update(self.pool.get(i).default_get(cursor, user,
fields_names, context=context))
# get the default values defined in the object
for field in fields_names:
if field in self._defaults:
value[field] = self._defaults[field](cursor, user, context)
if field in self._columns:
if isinstance(self._columns[field], fields.Property):
property_obj = self.pool.get('ir.property')
value[field] = property_obj.get(cursor, user, field,
self._name)
# get the default values set by the user and override the default
# values defined in the object
ir_default_obj = self.pool.get('ir.default')
defaults = ir_default_obj.get_default(cursor, user,
self._name, False, context=context)
for field, field_value in defaults.items():
if field in fields_names:
fld_def = (field in self._columns) and self._columns[field] \
or self._inherit_fields[field][2]
if fld_def._type in ('many2one', 'one2one'):
obj = self.pool.get(fld_def._obj)
if not obj.search(cursor, user, [('id', '=', field_value)]):
continue
if isinstance(field_value, (int, long)):
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)
field_value2 = []
for i in range(len(field_value)):
if not obj.search(cursor, user, [('id', '=',
field_value[i])]):
continue
field_value2.append(field_value[i])
field_value = field_value2
if fld_def._type in ('one2many'):
obj = self.pool.get(fld_def._obj)
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', 'one2one'):
obj2 = self.pool.get(obj._columns[field2]._obj)
if not obj2.search(cursor, user,
[('id', '=', field_value[i][field2])]):
continue
if isinstance(field_value[i][field2],
(int, long)):
field_value[i][field2] = obj2.name_get(
cursor, user,
field_value[i][field2],
context=context)[0]
# TODO add test for many2many and one2many
field_value2[i][field2] = field_value[i][field2]
field_value = field_value2
value[field] = field_value
value = self._default_on_change(cursor, user, value, context=context)
return value
def _default_on_change(self, cursor, user, value, context=None):
res = value.copy()
val = {}
for i in self._inherits.keys():
val.update(self.pool.get(i)._default_on_change(cursor, user,
value, context=context))
for field in value.keys():
if field in self._columns:
if self._columns[field].on_change:
args = {}
for arg in self._columns[field].on_change:
args[arg] = value.get(arg, False)
if arg in self._columns \
and self._columns[arg]._type == 'many2one':
if isinstance(args[arg], (list, tuple)):
args[arg] = args[arg][0]
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)
for val2 in res[field]:
val2.update(obj._default_on_change(cursor, user,
val2, context=context))
res.update(val)
return res
def unlink(self, cursor, user, ids, context=None):
if context is None:
context = {}
if not ids:
return True
if isinstance(ids, (int, long)):
ids = [ids]
if self._table_query:
return True
delta = context.get('read_delta', False)
if delta and self._log_access:
for i in range((len(ids) / ID_MAX) + \
((len(ids) % ID_MAX) and 1 or 0)):
sub_ids = ids[ID_MAX * i:ID_MAX * (i + 1)]
cursor.execute(
"SELECT (now() - min(write_date)) <= '%s'::interval " \
"FROM \"%s\" WHERE id in (%s)" % \
(delta, self._table,
",".join([str(x) for x in sub_ids])))
res = cursor.fetchone()
if res and res[0]:
raise ExceptORM('ConcurrencyException',
'This record was modified in the meanwhile')
del context['read_delta']
self.pool.get('ir.model.access').check(cursor, user, self._name,
'unlink')
cursor.execute(
"SELECT id FROM wkf_instance "\
"WHERE res_id IN (" + ",".join(["%s" for i in ids]) + ") "\
"AND res_type = %s AND state != 'complete'",
ids + [self._name])
if cursor.rowcount != 0:
raise ExceptORM(
'UserError','You cannot delete a record with a running workflow.')
wf_service = LocalService("workflow")
for obj_id in ids:
wf_service.trg_delete(user, self._name, obj_id, cursor)
#cursor.execute('select * from ' + self._table + \
# ' where id in ('+str_d+')', ids)
#res = cursor.dictfetchall()
#for key in self._inherits:
# ids2 = [x[self._inherits[key]] for x in res]
# self.pool.get(key).unlink(cursor, user, ids2)
domain1, domain2 = self.pool.get('ir.rule').domain_get(cursor, user,
self._name)
if domain1:
domain1 = ' AND ' + domain1
for i in range((len(ids) / ID_MAX) + \
((len(ids) % ID_MAX) and 1 or 0)):
sub_ids = ids[ID_MAX * i:ID_MAX * (i + 1)]
str_d = ','.join(('%s',) * len(sub_ids))
if domain1:
cursor.execute('SELECT id FROM "'+self._table+'" ' \
'WHERE id IN (' + str_d + ') ' + domain1,
sub_ids + domain2)
if not cursor.rowcount == len({}.fromkeys(sub_ids)):
raise ExceptORM('AccessError',
'You try to bypass an access rule ' \
'(Document type: %s).' % self._description)
cursor.execute('DELETE FROM inherit ' \
'WHERE (obj_type = %s AND obj_id IN ('+str_d+')) ' \
'OR (inst_type = %s AND inst_id IN ('+str_d+'))',
((self._name,) + tuple(sub_ids) + \
(self._name,) + tuple(sub_ids)))
if domain1:
cursor.execute('DELETE FROM "'+self._table+'" ' \
'WHERE id IN (' + str_d + ') ' + domain1,
sub_ids + domain2)
else:
cursor.execute('DELETE FROM "'+self._table+'" ' \
'WHERE id IN (' + str_d + ')', sub_ids)
return True
# TODO: Validate
def write(self, cursor, user, ids, vals, context=None):
if context is None:
context = {}
if not ids:
return True
if self._table_query:
return True
vals = vals.copy()
if isinstance(ids, (int, long)):
ids = [ids]
delta = context.get('read_delta', False)
if delta and self._log_access:
for i in range((len(ids) / ID_MAX) + \
((len(ids) % ID_MAX) and 1 or 0)):
sub_ids = ids[ID_MAX * i:ID_MAX * (i + 1)]
cursor.execute("SELECT " \
"(now() - min(write_date)) <= '%s'::interval"\
" FROM %s WHERE id IN (%s)" % \
(delta, self._table,
",".join([str(x) for x in sub_ids])))
res = cursor.fetchone()
if res and res[0]:
for field in vals:
if field in self._columns \
and self._columns[field]._classic_write:
raise ExceptORM('ConcurrencyException',
'This record was modified in the meanwhile')
del context['read_delta']
self.pool.get('ir.model.access').check(cursor, user, self._name,
'write')
if 'write_uid' in vals:
del vals['write_uid']
if 'write_date' in vals:
del vals['write_date']
if 'id' in vals:
del vals['id']
#for v in self._inherits.values():
# assert v not in vals, (v, vals)
upd0 = []
upd1 = []
upd_todo = []
updend = []
direct = []
for field in vals:
if field in self._columns:
if self._columns[field]._classic_write:
if (not self._columns[field].translate) \
or context.get('lang', 'en_US') == 'en_US':
upd0.append('"' + field + '"=' + \
self._columns[field]._symbol_set[0])
upd1.append(self._columns[field]._symbol_set[1](
vals[field]))
direct.append(field)
else:
upd_todo.append(field)
else:
updend.append(field)
if field in self._columns \
and hasattr(self._columns[field], 'selection') \
and vals[field]:
if self._columns[field]._type == 'reference':
val = vals[field].split(',')[0]
else:
val = vals[field]
if isinstance(self._columns[field].selection, (tuple, list)):
if val not in dict(self._columns[field].selection):
raise ExceptORM('ValidateError',
'The value "%s" for the field "%s" ' \
'is not in the selection' % \
(val, field))
else:
if val not in dict(getattr(self,
self._columns[field].selection)(
cursor, user, context=context)):
raise ExceptORM('ValidateError',
'The value "%s" for the field "%s" ' \
'is not in the selection' % \
(val, field))
if self._log_access:
upd0.append('write_uid = %s')
upd0.append('write_date = now()')
upd1.append(user)
if len(upd0):
domain1, domain2 = self.pool.get('ir.rule').domain_get(cursor,
user, self._name)
if domain1:
domain1 = ' and ' + domain1
for i in range((len(ids) / ID_MAX) + \
((len(ids) % ID_MAX) and 1 or 0)):
sub_ids = ids[ID_MAX * i:ID_MAX * (i + 1)]
ids_str = ','.join([str(x) for x in sub_ids])
if domain1:
cursor.execute('SELECT id FROM "' + self._table + '" ' \
'WHERE id IN (' + ids_str + ') ' + domain1, domain2)
if not cursor.rowcount == len({}.fromkeys(sub_ids)):
raise ExceptORM('AccessError',
'You try to bypass an access rule ' \
'(Document type: %s).' % \
self._description)
else:
cursor.execute('SELECT id FROM "' + self._table + '" ' \
'WHERE id IN (' + ids_str + ')')
if not cursor.rowcount == len({}.fromkeys(sub_ids)):
raise ExceptORM('AccessError',
'You try to bypass an access rule ' \
'(Document type: %s).' % \
self._description)
if domain1:
cursor.execute('UPDATE "' + self._table + '" ' \
'SET ' + ','.join(upd0) + ' ' \
'WHERE id IN (' + ids_str + ') ' + domain1,
upd1 + domain2)
else:
cursor.execute('UPDATE "' + self._table + '" ' \
'SET ' + ','.join(upd0) + ' ' \
'WHERE id IN (' + ids_str + ') ', upd1)
for field in direct:
if self._columns[field].translate:
self.pool.get('ir.translation')._set_ids(cursor, user,
self._name + ',' + field, 'model',
context.get('language','en_US'), ids, vals[field])
# call the 'set' method of fields which are not classic_write
upd_todo.sort(lambda x, y: self._columns[x].priority - \
self._columns[y].priority)
for field in upd_todo:
for select_id in ids:
self._columns[field].set(cursor, self, select_id, field,
vals[field], user, context=context)
for table in self._inherits:
col = self._inherits[table]
nids = []
for i in range((len(ids) / ID_MAX) + \
((len(ids) % ID_MAX) and 1 or 0)):
sub_ids = ids[ID_MAX * i:ID_MAX * (i +1)]
ids_str = ','.join([str(x) for x in sub_ids])
cursor.execute('SELECT DISTINCT "' + col + '" ' \
'FROM "' + self._table + '" WHERE id IN (' + ids_str + ')',
upd1)
nids.extend([x[0] for x in cursor.fetchall()])
vals2 = {}
for val in updend:
if self._inherit_fields[val][0] == table:
vals2[val] = vals[val]
self.pool.get(table).write(cursor, user, nids, vals2,
context=context)
self._validate(cursor, user, ids)
# Restart rule cache
if self.pool.get('ir.rule.group').search(cursor, 0, [
('model.model', '=', self._name),
], context=context):
self.pool.get('ir.rule').domain_get(cursor.dbname)
wf_service = LocalService("workflow")
for obj_id in ids:
wf_service.trg_write(user, self._name, obj_id, cursor)
return True
def __clean_defaults(self, defaults):
vals = {}
for field in defaults.keys():
fld_def = (field in self._columns) and self._columns[field] \
or self._inherit_fields[field][2]
if fld_def._type in ('many2one', 'one2one'):
if isinstance(defaults[field], (list, tuple)):
vals[field] = defaults[field][0]
else:
vals[field] = defaults[field]
elif fld_def._type in ('one2many',):
obj = self.pool.get(self._columns[field]._obj)
vals[field] = []
for defaults2 in defaults[field]:
vals2 = obj.__clean_defaults(defaults2)
vals[field].append(('create', vals2))
elif fld_def._type in ('many2many',):
vals[field] = [('set', defaults[field])]
else:
vals[field] = defaults[field]
return vals
def create(self, cursor, user, vals, context=None):
"""
cursor = database cursor
user = user id
vals = dictionary of the form {'field_name': field_value, ...}
"""
if self._table_query:
return False
vals = vals.copy()
self.pool.get('ir.model.access').check(cursor, user, self._name,
'create')
if 'create_uid' in vals:
del vals['create_uid']
if 'create_date' in vals:
del vals['create_date']
if 'id' in vals:
del vals['id']
default = []
avoid_table = []
for (i, j) in self._inherits.items():
if j in vals:
avoid_table.append(i)
for i in self._columns.keys(): # + self._inherit_fields.keys():
if not i in vals \
and i not in ('create_uid', 'create_date',
'write_uid', 'write_date'):
default.append(i)
for i in self._inherit_fields.keys():
if (not i in vals) \
and (not self._inherit_fields[i][0] in avoid_table):
default.append(i)
if len(default):
defaults = self.default_get(cursor, user, default, context)
vals.update(self.__clean_defaults(defaults))
tocreate = {}
for i in self._inherits:
if self._inherits[i] not in vals:
tocreate[i] = {}
(upd0, upd1, upd2) = ('', '', [])
upd_todo = []
for i in vals.keys():
if i in self._inherit_fields:
(table, col, col_detail) = self._inherit_fields[i]
tocreate[table][i] = vals[i]
del vals[i]
cursor.execute("SELECT NEXTVAL('" + self._sequence + "')")
(id_new,) = cursor.fetchone()
for table in tocreate:
new_id = self.pool.get(table).create(cursor, user, tocreate[table])
upd0 += ',' + self._inherits[table]
upd1 += ',%s'
upd2.append(new_id)
cursor.execute('INSERT INTO inherit ' \
'(obj_type, obj_id, inst_type, inst_id) ' \
'values (%s, %s, %s, %s)',
(table, new_id, self._name, id_new))
for field in vals:
if self._columns[field]._classic_write:
upd0 = upd0 + ',"' + field + '"'
upd1 = upd1 + ',' + self._columns[field]._symbol_set[0]
upd2.append(self._columns[field]._symbol_set[1](vals[field]))
else:
upd_todo.append(field)
if field in self._columns \
and hasattr(self._columns[field], 'selection') \
and vals[field]:
if self._columns[field]._type == 'reference':
val = vals[field].split(',')[0]
else:
val = vals[field]
if isinstance(self._columns[field].selection, (tuple, list)):
if val not in dict(self._columns[field].selection):
raise ExceptORM('ValidateError',
'The value "%s" for the field "%s" ' \
'is not in the selection' % \
(val, field))
else:
if val not in dict(getattr(self,
self._columns[field].selection)(
cursor, user, context=context)):
raise ExceptORM('ValidateError',
'The value "%s" for the field "%s" ' \
'is not in the selection' % \
(val, field))
if self._log_access:
upd0 += ', create_uid, create_date'
upd1 += ', %s, now()'
upd2.append(user)
cursor.execute('INSERT INTO "' + self._table + '" ' \
'(id' + upd0 + ') ' \
'VALUES (' + str(id_new) + upd1 + ')', tuple(upd2))
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, vals[field],
user, context)
self._validate(cursor, user, [id_new])
wf_service = LocalService("workflow")
wf_service.trg_create(user, self._name, id_new, cursor)
return id_new
def fields_get(self, cursor, user, fields_names=None, context=None):
"""
returns the definition of each field in the object
the optional fields_names parameter can limit the result to some fields
"""
if context is None:
context = {}
res = {}
translation_obj = self.pool.get('ir.translation')
model_access_obj = self.pool.get('ir.model.access')
for parent in self._inherits:
res.update(self.pool.get(parent).fields_get(cursor, user,
fields_names, context))
write_access = model_access_obj.check(cursor, user, self._name, 'write',
raise_exception=False)
if self._table_query:
write_access = False
#Add translation to cache
trans_args = []
for field in self._columns.keys():
trans_args.append((self._name + ',' + field, 'field',
context.get('language', 'en_US'), None))
trans_args.append((self._name + ',' + field, 'help',
context.get('language', 'en_US'), None))
if hasattr(self._columns[field], 'selection'):
if isinstance(self._columns[field].selection, (tuple, list)):
sel = self._columns[field].selection
for (key, val) in sel:
trans_args.append((self._name + ',' + field,
'selection', context.get('language', 'en_US'),
val))
translation_obj._get_sources(cursor, trans_args)
for field in self._columns.keys():
res[field] = {'type': self._columns[field]._type}
for arg in (
'string',
'readonly',
'states',
'size',
'required',
'change_default',
'translate',
'help',
'select',
'on_change',
):
if getattr(self._columns[field], arg, False):
res[field][arg] = getattr(self._columns[field], arg)
if not write_access:
res[field]['readonly'] = True
res[field]['states'] = {}
for arg in ('digits', 'invisible'):
if hasattr(self._columns[field], arg) \
and getattr(self._columns[field], arg):
res[field][arg] = getattr(self._columns[field], arg)
# translate the field label
res_trans = translation_obj._get_source(cursor,
self._name + ',' + field, 'field',
context.get('language', 'en_US'))
if res_trans:
res[field]['string'] = res_trans
help_trans = translation_obj._get_source(cursor,
self._name + ',' + field, 'help',
context.get('language', 'en_US'))
if help_trans:
res[field]['help'] = help_trans
if hasattr(self._columns[field], 'selection'):
if isinstance(self._columns[field].selection, (tuple, list)):
sel = self._columns[field].selection
# translate each selection option
sel2 = []
for (key, val) in sel:
val2 = translation_obj._get_source(cursor,
self._name + ',' + field, 'selection',
context.get('language', 'en_US'), val)
sel2.append((key, val2 or val))
sel = sel2
res[field]['selection'] = sel
else:
# call the 'dynamic selection' function
res[field]['selection'] = self._columns[field].selection
if res[field]['type'] in (
'one2many',
'many2many',
'many2one',
'one2one',
):
res[field]['relation'] = self._columns[field]._obj
res[field]['domain'] = self._columns[field]._domain
res[field]['context'] = self._columns[field]._context
if res[field]['type'] == 'one2many':
res[field]['relation_field'] = self._columns[field]._field
if fields_names:
# filter out fields which aren't in the fields_names list
for i in res.keys():
if i not in fields_names:
del res[i]
return res
def view_header_get(self, cursor, user, value, view_type='form',
context=None):
"""
Overload this method if you need a window title
which depends on the context
"""
return False
def __view_look_dom(self, cursor, user, node, context=None):
if context is None:
context = {}
result = False
fields_attrs = {}
childs = True
if node.nodeType == node.ELEMENT_NODE \
and node.localName in ('field', 'label', 'separator'):
if node.hasAttribute('name'):
attrs = {}
try:
if node.getAttribute('name') in self._columns:
relation = self._columns[node.getAttribute('name')]._obj
else:
relation = self._inherit_fields[node.getAttribute(
'name')][2]._obj
except:
relation = False
if relation:
childs = False
views = {}
for field in node.childNodes:
if field.nodeType == field.ELEMENT_NODE \
and field.localName in ('form', 'tree', 'graph'):
node.removeChild(field)
xarch, xfields = self.pool.get(relation
)._view_look_dom_arch(cursor, user, field,
context)
views[str(field.localName)] = {
'arch': xarch,
'fields': xfields
}
attrs = {'views': views}
fields_attrs[node.getAttribute('name')] = attrs
elif node.nodeType == node.ELEMENT_NODE \
and node.localName in ('form', 'tree', 'graph'):
value = ''
if node.hasAttribute('string'):
value = node.getAttribute('string').encode('utf8')
result = self.view_header_get(cursor, user, value, node.localName,
context)
if result:
node.setAttribute('string', result.decode('utf8'))
if node.nodeType == node.ELEMENT_NODE:
# translate view
translation_obj = self.pool.get('ir.translation')
if ('language' in context) and not result:
if node.hasAttribute('string') and node.getAttribute('string'):
trans = translation_obj._get_source(cursor,
self._name, 'view', context['language'],
node.getAttribute('string').encode('utf8'))
if trans:
node.setAttribute('string', trans.decode('utf8'))
if node.hasAttribute('sum') and node.getAttribute('sum'):
trans = translation_obj._get_source(cursor,
self._name, 'view', context['language'],
node.getAttribute('sum').encode('utf8'))
if trans:
node.setAttribute('sum', trans.decode('utf8'))
# Add view for properties !
if node.localName == 'properties':
parent = node.parentNode
doc = node.ownerDocument
models = ["'" + x + "'" for x in [self._name] + \
self._inherits.keys()]
cursor.execute('SELECT f.name AS name, ' \
'f.group_name AS group_name ' \
'FROM ir_model_field AS f, ir_model AS m ' \
'WHERE f.model = m.id ' \
'AND m.model in (' + ','.join(models) + ') ' \
'AND f.view_load ORDER BY f.group_name, f.id')
oldgroup = None
for fname, gname in cursor.fetchall():
if oldgroup != gname:
child = doc.createElement('separator')
child.setAttribute('string', gname.decode('utf8'))
child.setAttribute('colspan', "4")
oldgroup = gname
parent.insertBefore(child, node)
child = doc.createElement('label')
child.setAttribute('name', fname.decode('utf8'))
parent.insertBefore(child, node)
child = doc.createElement('field')
child.setAttribute('name', fname.decode('utf8'))
parent.insertBefore(child, node)
parent.removeChild(node)
if childs:
for field in node.childNodes:
fields_attrs.update(self.__view_look_dom(cursor, user, field,
context))
return fields_attrs
def _view_look_dom_arch(self, cursor, user, node, context=None):
fields_def = self.__view_look_dom(cursor, user, node, context=context)
arch = node.toxml(encoding="utf-8").replace('\t', '')
fields2 = self.fields_get(cursor, user, fields_def.keys(), context)
for field in fields_def:
if field in fields2:
fields2[field].update(fields_def[field])
return arch, fields2
def fields_view_get(self, cursor, user, view_id=None, view_type='form',
context=None, toolbar=False, hexmd5=None):
def _inherit_apply(src, inherit):
def _find(node, node2):
if node2.nodeType == node2.ELEMENT_NODE \
and node2.localName == 'xpath':
res = xpath.Evaluate(node2.getAttribute('expr'), node)
return res and res[0] or None
return None
doc_src = dom.minidom.parseString(src)
doc_dest = dom.minidom.parseString(inherit)
parent_node = doc_dest.firstChild
for node2 in parent_node.childNodes:
if not node2.nodeType == node2.ELEMENT_NODE:
continue
node = _find(doc_src, node2)
if node:
pos = 'inside'
if node2.hasAttribute('position'):
pos = node2.getAttribute('position')
if pos == 'replace':
parent = node.parentNode
for child in node2.childNodes:
if child.nodeType == child.ELEMENT_NODE:
parent.insertBefore(child, node)
parent.removeChild(node)
else:
sib = node.nextSibling
for child in node2.childNodes:
if child.nodeType == child.ELEMENT_NODE:
if pos == 'inside':
node.appendChild(child)
elif pos == 'after':
if sib:
node.parentNode.insertBefore(child, sib)
else:
node.parentNode.appendChild(child)
elif pos == 'before':
node.parentNode.insertBefore(child, node)
else:
raise AttributeError, \
'Unknown position ' \
'in inherited view %s!' % pos
else:
attrs = ''.join([
' %s="%s"' % (attr, node2.getAttribute(attr))
for attr in node2.attributes.keys()
if attr != 'position'
])
tag = "<%s%s>" % (node2.localName, attrs)
raise AttributeError, \
"Couldn't find tag '%s' in parent view !" % tag
return doc_src.toxml(encoding="utf-8").replace('\t', '')
result = {'type': view_type, 'model': self._name}
test = True
model = True
sql_res = False
while test:
if view_id:
where = (model and (" and model='%s'" % (self._name,))) or ''
cursor.execute('SELECT arch, name, field_childs, id, type, ' \
'inherit ' \
'FROM ir_ui_view WHERE id = %s ' + where, (view_id,))
else:
cursor.execute('SELECT arch, name, field_childs, id, type, ' \
'inherit ' \
'FROM ir_ui_view ' \
'WHERE model = %s AND type = %s AND inherit IS NULL ' \
'ORDER BY priority',
(self._name,view_type))
sql_res = cursor.fetchone()
if not sql_res:
break
test = sql_res[5]
view_id = test or sql_res[3]
model = False
# if a view was found
if sql_res:
result['type'] = sql_res[4]
result['view_id'] = sql_res[3]
result['arch'] = sql_res[0]
def _inherit_apply_rec(result, inherit_id):
# get all views which inherit from (ie modify) this view
cursor.execute('SELECT arch, id FROM ir_ui_view ' \
'WHERE inherit = %s AND model = %s ' \
'ORDER BY priority', (inherit_id, self._name))
sql_inherit = cursor.fetchall()
for (inherit, view_id) in sql_inherit:
result = _inherit_apply(result, inherit)
result = _inherit_apply_rec(result, view_id)
return result
result['arch'] = _inherit_apply_rec(result['arch'], sql_res[3])
result['name'] = sql_res[1]
result['field_childs'] = sql_res[2] or False
# otherwise, build some kind of default view
else:
if view_type == 'form':
res = self.fields_get(cursor, user, context=context)
xml = '''<?xml version="1.0" encoding="utf-8"?>''' \
'''<form string="%s">''' % (self._description,)
for i in res:
if i in ('create_uid', 'create_date', 'write_uid', 'write_date'):
continue
if res[i]['type'] not in ('one2many', 'many2many'):
xml += '<label name="%s"/>' % (i,)
xml += '<field name="%s"/>' % (i,)
if res[i]['type'] == 'text':
xml += "<newline/>"
xml += "</form>"
elif view_type == 'tree':
xml = '''<?xml version="1.0" encoding="utf-8"?>''' \
'''<tree string="%s"><field name="%s"/></tree>''' \
% (self._description, self._rec_name)
elif view_type == 'calendar':
xml = '''<?xml version="1.0" encoding="utf-8"?>''' \
'''<calendar string="%s" date_start="%s">''' \
'''<field name="%s"/></calendar>''' \
% (self._description, self._date_name, self._rec_name)
else:
xml = ''
result['arch'] = xml
result['name'] = 'default'
result['field_childs'] = False
result['view_id'] = 0
doc = dom.minidom.parseString(result['arch'])
xarch, xfields = self._view_look_dom_arch(cursor, user, doc,
context=context)
result['arch'] = xarch
result['fields'] = xfields
if toolbar:
action_obj = self.pool.get('ir.action.keyword')
prints = action_obj.get_keyword(cursor, user, 'form_print',
(self._name, 0), context=context)
actions = action_obj.get_keyword(cursor, user, 'form_action',
(self._name, 0), context=context)
relates = action_obj.get_keyword(cursor, user, 'form_relate',
(self._name, 0), context=context)
result['toolbar'] = {
'print': prints,
'action': actions,
'relate': relates,
}
result['md5'] = md5.new(str(result)).hexdigest()
if hexmd5 == result['md5']:
return True
return result
fields_view_get = Cache('orm.fields_view_get')(fields_view_get)
def _where_calc(self, cursor, user, args, active_test=True, context=None):
if context is None:
context = {}
args = args[:]
# if the object has a field named 'active', filter out all inactive
# records unless they were explicitely asked for
if 'active' in self._columns \
and (active_test and context.get('active_test', True)):
i = 0
active_found = False
while i < len(args):
if args[i][0] == 'active':
active_found = True
i += 1
if not active_found:
args.append(('active', '=', 1))
i = 0
table_query = ''
if self._table_query:
table_query = '(' + self._table_query + ') AS '
tables = [table_query + '"' + self._table + '"']
joins = []
while i < len(args):
if args[i][1] not in (
'child_of',
'=',
'like',
'ilike',
'',
'!=',
'in',
'<=',
'>=',
'<',
'>'):
raise ExceptORM("Argument '%s' not supported"% args[i][1],"")
table = self
if args[i][0] in self._inherit_fields:
itable = self.pool.get(self._inherit_fields[args[i][0]][0])
table_query = ''
if itable._table_query:
table_query = '(' + self._table_query + ') AS '
if (table_query + '"' + itable._table + '"' not in tables):
tables.append(table_query + '"' + itable._table + '"')
joins.append(('id', 'join', '%s.%s' % \
(self._table, self._inherits[itable._name]), itable))
fargs = args[i][0].split('.', 1)
field = table._columns.get(fargs[0], False)
if not field:
if fargs[0] in self._inherit_fields:
table = self.pool.get(self._inherit_fields[args[i][0]][0])
field = table._columns.get(fargs[0], False)
if len(fargs) > 1:
if field._type == 'many2one':
args[i] = (fargs[0], 'inselect',
self.pool.get(field._obj).search(cursor, user,
[(fargs[1], args[i][1], args[i][2])],
context=context, query_string=True))
i += 1
continue
else:
i += 1
continue
if field._properties:
arg = [args.pop(i)]
j = i
while j < len(args):
if args[j][0] == arg[0][0]:
arg.append(args.pop(j))
else:
j += 1
if field._fnct_search:
args.extend(field.search(cursor, user, self,
arg[0][0], arg, context=context))
elif field._type == 'one2many':
field_obj = self.pool.get(field._obj)
if isinstance(args[i][2], basestring):
# get the ids of the records of the "distant" resource
ids2 = [x[0] for x in field_obj.name_search(cursor, user,
args[i][2], [], args[i][1], context=context)]
else:
ids2 = args[i][2]
if not ids2:
args[i] = ('id', '=', '0')
else:
table_query = ''
if field_obj._table_query:
table_query = '(' + field_obj._table_query + ') AS '
if len(ids2) < ID_MAX:
query1 = 'SELECT "' + field._field + '" ' \
'FROM ' + table_query + '"' + field_obj._table + '" ' \
'WHERE id IN (' + \
','.join(['%s' for x in ids2]) + ')'
query2 = [str(x) for x in ids2]
args[i] = ('id', 'inselect', (query1, query2))
else:
ids3 = []
for i in range((len(ids2) / ID_MAX) + \
(len(ids2) % ID_MAX)):
sub_ids = ids2[ID_MAX * i:ID_MAX * (i + 1)]
cursor.execute(
'SELECT "' + field._field + \
'" FROM ' + table_query + '"' + field_obj._table + '" ' \
'WHERE id IN (' + \
','.join(['%s' for x in sub_ids2]) + ')',
[str(x) for x in sub_ids2])
ids3.extend([x[0] for x in cursor.fetchall()])
args[i] = ('id', 'in', ids3)
i += 1
elif field._type == 'many2many':
# XXX must find a solution for long id list
if args[i][1] == 'child_of':
if isinstance(args[i][2], basestring):
ids2 = [x[0] for x in self.pool.get(
field._obj).name_search(cursor, user, args[i][2], [],
'like', context=context)]
else:
ids2 = args[i][2]
def _rec_get(ids, table, parent):
if not ids:
return []
ids2 = table.search(cursor, user,
[(parent, 'in', ids)], context=context)
return ids + _rec_get(ids2, table, parent)
def _rec_convert(ids):
if self.pool.get(field._obj)==self:
return ids
if not len(ids):
return []
cursor.execute(
'SELECT "' + field._id1 + '" ' \
'FROM "' + field._rel + '" ' \
'WHERE "' + field._id2 + '" IN (' + \
','.join(['%s' for x in ids]) + ')',
[str(x) for x in ids])
ids = [x[0] for x in cursor.fetchall()]
return ids
args[i] = ('id', 'in', _rec_convert(ids2 + _rec_get(ids2,
self.pool.get(field._obj),
self.pool.get(field._obj)._parent_name)))
else:
if isinstance(args[i][2], basestring):
res_ids = [x[0] for x in self.pool.get(field._obj
).name_search(cursor, user, args[i][2], [],
args[i][1], context=context)]
else:
res_ids = args[i][2]
if res_ids == True or res_ids == False:
query1 = 'SELECT "' + field._id1 + '" ' \
'FROM "' + field._rel + '"'
query2 = []
clause = 'inselect'
if res_ids == False:
clause = 'notinselect'
args[i] = ('id', clause, (query1, query2))
elif not res_ids:
args[i] = ('id', '=', '0')
else:
query1 = 'SELECT "' + field._id1 + '" ' \
'FROM "' + field._rel + '" ' \
'WHERE "' + field._id2 + '" IN (' + \
','.join(['%s' for x in res_ids]) + ')'
query2 = [str(x) for x in res_ids]
args[i] = ('id', 'inselect', (query1, query2))
i += 1
elif field._type == 'many2one':
# XXX must find a solution for long id list
if args[i][1] == 'child_of':
if isinstance(args[i][2], basestring):
ids2 = [x[0] for x in self.pool.get(
field._obj).name_search(cursor, user, args[i][2],
[], 'like', context=context)]
else:
ids2 = args[i][2]
def _rec_get(ids, table, parent):
if not ids:
return []
ids2 = table.search(cursor, user,
[(parent, 'in', ids)], context=context)
return ids + _rec_get(ids2, table, parent)
if field._obj != table._name:
args[i] = (args[i][0], 'in', ids2 + _rec_get(ids2,
self.pool.get(field._obj), table._parent_name),
table)
else:
args[i] = ('id', 'in', ids2 + _rec_get(ids2, table,
args[i][0]), table)
else:
if isinstance(args[i][2], basestring):
res_ids = self.pool.get(field._obj).name_search(cursor,
user, args[i][2], [], args[i][1],
context=context)
args[i] = (args[i][0], 'in', [x[0] for x in res_ids],
table)
else:
args[i] += (table,)
i += 1
else:
if field.translate:
if args[i][1] in ('like', 'ilike'):
args[i] = (args[i][0], args[i][1],
'%%%s%%' % args[i][2])
query1 = '(SELECT res_id FROM ir_translation ' \
'WHERE name = %s AND lang = %s ' \
'AND type = %s ' \
'AND VALUE ' + args[i][1] + ' %s)'
query2 = [table._name + ',' + args[i][0],
context.get('language', False) or 'en_US',
'model', args[i][2]]
query1 += ' UNION '
table_query = ''
if table._table_query:
table_query = '(' + table._table_query + ') AS '
query1 += '(SELECT id FROM ' + table_query + '"' + table._table + '" ' \
'WHERE "' + args[i][0] + '" ' + \
args[i][1] + ' %s)'
query2 += [args[i][2]]
args[i] = ('id', 'inselect', (query1, query2), table)
else:
args[i] += (table,)
i += 1
args.extend(joins)
qu1, qu2 = [], []
for arg in args:
table = self
if len(arg) > 3:
table = arg[3]
if arg[1] in ('inselect', 'notinselect'):
clause = 'IN'
if arg[1] == 'notinselect':
clause = 'NOT IN'
qu1.append('(%s.%s %s (%s))' % (table._table, arg[0], clause,
arg[2][0]))
qu2 += arg[2][1]
elif arg[1] == 'in':
if len(arg[2]) > 0:
todel = []
for xitem in range(len(arg[2])):
if arg[2][xitem] == False \
and isinstance(arg[2][xitem],bool):
todel.append(xitem)
for xitem in todel[::-1]:
del arg[2][xitem]
#TODO fix max_stack_depth
if len(arg[2]):
if arg[0] == 'id':
qu1.append('(%s.id in (%s))' % \
(table._table,
','.join(['%s'] * len(arg[2])),))
else:
qu1.append('(%s.%s in (%s))' % \
(table._table, arg[0], ','.join(
[table._columns[arg[0]].\
_symbol_set[0]] * len(arg[2]))))
if todel:
qu1[-1] = '(' + qu1[-1] + ' or ' + arg[0] + ' is null)'
qu2 += arg[2]
elif todel:
qu1.append('(' + arg[0] + ' IS NULL)')
else:
qu1.append(' false')
else:
if (arg[2] is False) and (arg[1] == '='):
qu1.append('(%s.%s IS NULL)' % \
(table._table, arg[0]))
elif (arg[2] is False) and (arg[1] == '<>' or arg[1] == '!='):
qu1.append('(%s.%s IS NOT NULL)' % \
(table._table, arg[0]))
else:
if arg[0] == 'id':
if arg[1] == 'join':
qu1.append('(%s.%s = %s)' % \
(table._table, arg[0], arg[2]))
else:
qu1.append('(%s.%s %s %%s)' % \
(table._table, arg[0], arg[1]))
qu2.append(arg[2])
else:
add_null = False
if arg[1] in ('like', 'ilike'):
if isinstance(arg[2], str):
str_utf8 = arg[2]
elif isinstance(arg[2], unicode):
str_utf8 = arg[2].encode('utf-8')
else:
str_utf8 = str(arg[2])
qu2.append('%%%s%%' % str_utf8)
if not str_utf8:
add_null = True
else:
if arg[0] in table._columns:
qu2.append(table._columns[arg[0]].\
_symbol_set[1](arg[2]))
if arg[0] in table._columns:
if arg[1] in ('like', 'ilike'):
qu1.append('(%s.%s %s %s)' % (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]))
else:
qu1.append('(%s.%s %s \'%s\')' % \
(table._table, arg[0], arg[1], arg[2]))
if add_null:
qu1[-1] = '('+qu1[-1]+' or '+arg[0]+' is null)'
return (qu1, qu2, tables)
def search_count(self, cursor, user, args, context=None):
res = self.search(cursor, user, args, context=context, count=True)
if isinstance(res, list):
return len(res)
return res
def search(self, cursor, user, args, offset=0, limit=None, order=None,
context=None, count=False, query_string=False):
# compute the where, order by, limit and offset clauses
(qu1, qu2, tables) = self._where_calc(cursor, user, args,
context=context)
if len(qu1):
qu1 = ' WHERE ' + ' AND '.join(qu1)
else:
qu1 = ''
order_by = []
for field, otype in (order or self._order):
if otype.upper() not in ('DESC', 'ASC'):
raise ExceptORM('Error', 'Wrong order type (%s)!' % otype)
if (field not in self._columns \
or not self._columns[field]._classic_write) \
and (field not in self._inherit_fields.keys() \
or not self._inherit_fields[field][2]._classic_write):
raise ExceptORM('Error', 'Wrong field name (%s) in order!' \
% field)
if field in self._inherit_fields.keys():
obj = self.pool.get(self._inherit_fields[field][0])
order_by.append(obj._table + '.' + field + ' ' + otype)
if '"' + obj._table + '"' not in tables:
tables.append('"' + obj._table + '"')
if len(qu1):
qu1 += ' AND '
else:
qu1 = ' WHERE '
qu1 += ' %s.%s = %s.id' % (self._table,
self._inherits[obj._name], obj._table)
else:
order_by.append(self._table + '.' + field + ' ' + otype)
order_by = ','.join(order_by)
limit_str = limit and (type(limit) in (float, int, long))\
and ' LIMIT %d' % limit or ''
offset_str = offset and (type(offset) in (float, int, long))\
and ' OFFSET %d' % offset or ''
# construct a clause for the rules :
domain1, domain2 = self.pool.get('ir.rule').domain_get(cursor, user,
self._name)
if domain1:
qu1 = qu1 and qu1 + ' AND ' + domain1 or ' WHERE ' + domain1
qu2 += domain2
if count:
cursor.execute('SELECT COUNT(%s.id) FROM ' % self._table +
','.join(tables) + qu1 + limit_str + offset_str, qu2)
res = cursor.fetchall()
return res[0][0]
# execute the "main" query to fetch the ids we were searching for
query_str = 'SELECT %s.id FROM ' % self._table + \
','.join(tables) + qu1 + ' order by ' + order_by + \
limit_str + offset_str
if query_string:
return (query_str, qu2)
cursor.execute(query_str, qu2)
res = cursor.fetchall()
return [x[0] for x in res]
def name_get(self, cursor, user, ids, context=None):
if not ids:
return []
if isinstance(ids, (int, long)):
ids = [ids]
return [(r['id'], str(r[self._rec_name])) for r in self.read(cursor,
user, ids, [self._rec_name], context, load='_classic_write')]
def name_search(self, cursor, user, name='', args=None, operator='ilike',
context=None, limit=None):
if args is None:
args = []
args = args[:]
if name:
args += [(self._rec_name, operator, name)]
ids = self.search(cursor, user, args, limit=limit, context=context)
res = self.name_get(cursor, user, ids, context=context)
return res
def copy(self, cursor, user, object_id, default=None, context=None):
if default is None:
default = {}
if 'state' not in default:
if 'state' in self._defaults:
default['state'] = self._defaults['state'](cursor, user,
context)
data = self.read(cursor, user, object_id, context=context)
fields2 = self.fields_get(cursor, user)
for field in fields2:
ftype = fields2[field]['type']
if self._log_access \
and (field in (
'create_date',
'create_uid',
'write_date',
'write_uid',
)):
del data[field]
if field in default:
data[field] = default[field]
elif ftype == 'function':
del data[field]
elif ftype == 'many2one':
try:
data[field] = data[field] and data[field][0]
except:
pass
elif ftype in ('one2many', 'one2one'):
res = []
rel = self.pool.get(fields2[field]['relation'])
for rel_id in data[field]:
# the lines are first duplicated using the wrong (old)
# parent but then are reassigned to the correct one thanks
# to the ('add', ...)
res.append(('add', rel.copy(cursor, user, rel_id,
context=context)))
data[field] = res
elif ftype == 'many2many':
data[field] = [('set', data[field])]
del data['id']
for i in self._inherits:
del data[self._inherits[i]]
return self.create(cursor, user, data)
def check_recursion(self, cursor, user, ids, parent=None):
if parent is None:
parent = self._parent_name
ids_parent = ids[:]
while len(ids_parent):
ids_parent2 = []
for i in range((len(ids) / ID_MAX) + \
((len(ids) % ID_MAX) and 1 or 0)):
sub_ids_parent = ids_parent[ID_MAX * i:ID_MAX * (i + 1)]
cursor.execute('SELECT distinct "' + parent + '" ' +
'FROM "' + self._table + '" ' +
'WHERE id IN ' \
'(' + ','.join([str(x) for x in sub_ids_parent]) + ')')
ids_parent2.extend(filter(None,
[x[0] for x in cursor.fetchall()]))
ids_parent = ids_parent2
for i in ids_parent:
if i in ids:
return False
return True
def default_sequence(self, cursor, user, context=None):
cursor.execute('SELECT MAX(sequence) ' \
'FROM "' + self._table + '"')
res = cursor.fetchone()
if res:
return res[0]
return 0
orm = ORM