[REF] orm: merged orm_template inside orm.

Things could be re-ordered but this way the diff remains small.

bzr revid: vmt@openerp.com-20110812142814-6dbd0wodllw8xn37
This commit is contained in:
Vo Minh Thu 2011-08-12 16:28:14 +02:00
parent 7797702a9f
commit 80f6562618
1 changed files with 187 additions and 245 deletions

View File

@ -562,7 +562,7 @@ class MetaModel(type):
self.module_to_models.setdefault(self._module, []).append(self)
class orm_template(object):
class orm(object):
""" Base class for OpenERP models.
OpenERP models are created by inheriting from this class (although
@ -604,8 +604,14 @@ class orm_template(object):
_table = None
_invalids = set()
_log_create = False
_sql_constraints = []
_protected = ['read', 'write', 'create', 'default_get', 'perm_read', 'unlink', 'fields_get', 'fields_view_get', 'search', 'name_get', 'distinct_field_get', 'name_search', 'copy', 'import_data', 'search_count', 'exists']
__logger = logging.getLogger('orm')
__schema = logging.getLogger('orm.schema')
CONCURRENCY_CHECK_FIELD = '__last_update'
def log(self, cr, uid, id, message, secondary=False, context=None):
if context and context.get('disable_log'):
return True
@ -623,9 +629,6 @@ class orm_template(object):
"""Override this method to do specific things when a view on the object is opened."""
pass
def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False):
raise NotImplementedError(_('The read_group method is not implemented on this object !'))
def _field_create(self, cr, context=None):
""" Create entries in ir_model_fields for all the model's fields.
@ -728,13 +731,6 @@ class orm_template(object):
break
cr.commit()
def _auto_init(self, cr, context=None):
raise_on_invalid_object_name(self._name)
self._field_create(cr, context=context)
def _auto_end(self, cr, context=None):
pass
#
# Goal: try to apply inheritance at the instanciation level and
# put objects in the pool var
@ -829,7 +825,15 @@ class orm_template(object):
return None
def __init__(self, pool, cr):
""" Initialize a model and make it part of the given registry."""
""" Initialize a model and make it part of the given registry.
- copy the stored fields' functions in the osv_pool,
- update the _columns with the fields found in ir_model_fields,
- ensure there is a many2one for each _inherits'd parent,
- update the children's _columns,
- give a chance to each field to initialize itself.
"""
pool.add(self._name, self)
self.pool = pool
@ -846,26 +850,89 @@ class orm_template(object):
if not self._table:
self._table = self._name.replace('.', '_')
def browse(self, cr, uid, select, context=None, list_class=None, fields_process=None):
"""Fetch records as objects allowing to use dot notation to browse fields and relations
if not hasattr(self, '_log_access'):
# If _log_access is not specified, it is the same value as _auto.
self._log_access = getattr(self, "_auto", True)
:param cr: database cursor
:param user: current user id
:param select: id or list of ids.
:param context: context arguments, like lang, time zone
:rtype: object or list of objects requested
self._columns = self._columns.copy()
for store_field in self._columns:
f = self._columns[store_field]
if hasattr(f, 'digits_change'):
f.digits_change(cr)
if not isinstance(f, fields.function):
continue
if not f.store:
continue
if self._columns[store_field].store is True:
sm = {self._name: (lambda self, cr, uid, ids, c={}: ids, None, 10, None)}
else:
sm = self._columns[store_field].store
for object, aa in sm.items():
if len(aa) == 4:
(fnct, fields2, order, length) = aa
elif len(aa) == 3:
(fnct, fields2, order) = aa
length = None
else:
raise except_orm('Error',
('Invalid function definition %s in object %s !\nYou must use the definition: store={object:(fnct, fields, priority, time length)}.' % (store_field, self._name)))
self.pool._store_function.setdefault(object, [])
ok = True
for x, y, z, e, f, l in self.pool._store_function[object]:
if (x==self._name) and (y==store_field) and (e==fields2):
if f == order:
ok = False
if ok:
self.pool._store_function[object].append( (self._name, store_field, fnct, fields2, order, length))
self.pool._store_function[object].sort(lambda x, y: cmp(x[4], y[4]))
for (key, _, msg) in self._sql_constraints:
self.pool._sql_error[self._table+'_'+key] = msg
# Load manual fields
cr.execute("SELECT id FROM ir_model_fields WHERE name=%s AND model=%s", ('state', 'ir.model.fields'))
if cr.fetchone():
cr.execute('SELECT * FROM ir_model_fields WHERE model=%s AND state=%s', (self._name, 'manual'))
for field in cr.dictfetchall():
if field['name'] in self._columns:
continue
attrs = {
'string': field['field_description'],
'required': bool(field['required']),
'readonly': bool(field['readonly']),
'domain': eval(field['domain']) if field['domain'] else None,
'size': field['size'],
'ondelete': field['on_delete'],
'translate': (field['translate']),
'manual': True,
#'select': int(field['select_level'])
}
if field['ttype'] == 'selection':
self._columns[field['name']] = fields.selection(eval(field['selection']), **attrs)
elif field['ttype'] == 'reference':
self._columns[field['name']] = fields.reference(selection=eval(field['selection']), **attrs)
elif field['ttype'] == 'many2one':
self._columns[field['name']] = fields.many2one(field['relation'], **attrs)
elif field['ttype'] == 'one2many':
self._columns[field['name']] = fields.one2many(field['relation'], field['relation_field'], **attrs)
elif field['ttype'] == 'many2many':
_rel1 = field['relation'].replace('.', '_')
_rel2 = field['model'].replace('.', '_')
_rel_name = 'x_%s_%s_%s_rel' % (_rel1, _rel2, field['name'])
self._columns[field['name']] = fields.many2many(field['relation'], _rel_name, 'id1', 'id2', **attrs)
else:
self._columns[field['name']] = getattr(fields, field['ttype'])(**attrs)
self._inherits_check()
self._inherits_reload()
if not self._sequence:
self._sequence = self._table + '_id_seq'
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,)
for f in self._columns:
self._columns[f].restart()
"""
self._list_class = list_class or browse_record_list
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 browse_record(cr, uid, select, self, cache, context=context, list_class=self._list_class, fields_process=fields_process)
elif isinstance(select, list):
return self._list_class([browse_record(cr, uid, id, self, cache, context=context, list_class=self._list_class, fields_process=fields_process) for id in select], context=context)
else:
return browse_null()
def __export_row(self, cr, uid, row, fields, context=None):
if context is None:
@ -1219,30 +1286,6 @@ class orm_template(object):
self._parent_store_compute(cr)
return (position, 0, 0, 0)
def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'):
"""
Read records with given ids with the given fields
:param cr: database cursor
:param user: current user id
:param ids: id or list of the ids of the records to read
:param fields: optional list of field names to return (default: all fields would be returned)
:type fields: list (example ['field_name_1', ...])
:param context: optional context dictionary - it may contains keys for specifying certain options
like ``context_lang``, ``context_tz`` to alter the results of the call.
A special ``bin_size`` boolean flag may also be passed in the context to request the
value of all fields.binary columns to be returned as the size of the binary instead of its
contents. This can also be selectively overriden by passing a field-specific flag
in the form ``bin_size_XXX: True/False`` where ``XXX`` is the name of the field.
Note: The ``bin_size_XXX`` form is new in OpenERP v6.0.
:return: list of dictionaries((dictionary per record asked)) with requested field values
:rtype: [{name_of_the_field: value, ...}, ...]
:raise AccessError: * if user has no read rights on the requested object
* if user tries to bypass access rules for read on the requested object
"""
raise NotImplementedError(_('The read method is not implemented on this object !'))
def get_invalid_fields(self, cr, uid):
return list(self._invalids)
@ -1375,19 +1418,6 @@ class orm_template(object):
defaults[key[8:]] = context[key]
return defaults
def perm_read(self, cr, user, ids, context=None, details=True):
raise NotImplementedError(_('The perm_read method is not implemented on this object !'))
def unlink(self, cr, uid, ids, context=None):
raise NotImplementedError(_('The unlink method is not implemented on this object !'))
def write(self, cr, user, ids, vals, context=None):
raise NotImplementedError(_('The write method is not implemented on this object !'))
def create(self, cr, user, vals, context=None):
raise NotImplementedError(_('The create method is not implemented on this object !'))
def fields_get_keys(self, cr, user, context=None):
res = self._columns.keys()
# TODO I believe this loop can be replace by
@ -1396,56 +1426,6 @@ class orm_template(object):
res.extend(self.pool.get(parent).fields_get_keys(cr, user, context))
return res
def fields_get(self, cr, user, allfields=None, context=None, write_access=True):
""" Return the definition of each field.
The returned value is a dictionary (indiced by field name) of
dictionaries. The _inherits'd fields are included. The string,
help, and selection (if present) attributes are translated.
"""
if context is None:
context = {}
res = {}
translation_obj = self.pool.get('ir.translation')
for parent in self._inherits:
res.update(self.pool.get(parent).fields_get(cr, user, allfields, context))
for f, field in self._columns.iteritems():
if allfields and f not in allfields:
continue
res[f] = fields.field_to_dict(self, cr, user, context, field)
if not write_access:
res[f]['readonly'] = True
res[f]['states'] = {}
if 'string' in res[f]:
res_trans = translation_obj._get_source(cr, user, self._name + ',' + f, 'field', context.get('lang', False) or 'en_US')
if res_trans:
res[f]['string'] = res_trans
if 'help' in res[f]:
help_trans = translation_obj._get_source(cr, user, self._name + ',' + f, 'help', context.get('lang', False) or 'en_US')
if help_trans:
res[f]['help'] = help_trans
if 'selection' in res[f]:
if isinstance(field.selection, (tuple, list)):
sel = field.selection
sel2 = []
for key, val in sel:
val2 = None
if val:
val2 = translation_obj._get_source(cr, user, self._name + ',' + f, 'selection', context.get('lang', False) or 'en_US', val)
sel2.append((key, val2 or val))
res[f]['selection'] = sel2
return res
#
# Overload this method if you need a window title which depends on the context
#
@ -2089,17 +2069,6 @@ class orm_template(object):
"""
return self._search(cr, user, args, offset=offset, limit=limit, order=order, context=context, count=count)
def _search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False, access_rights_uid=None):
"""
Private implementation of search() method, allowing specifying the uid to use for the access right check.
This is useful for example when filling in the selection list for a drop-down and avoiding access rights errors,
by specifying ``access_rights_uid=1`` to bypass access rights check, but not ir.rules!
:param access_rights_uid: optional user ID to use when checking access rights
(not for ir.rules, this is only for ir.model.access)
"""
raise NotImplementedError(_('The search method is not implemented on this object !'))
def name_get(self, cr, user, ids, context=None):
"""
@ -2173,12 +2142,6 @@ class orm_template(object):
res = self.name_get(cr, access_rights_uid, ids, context)
return res
def copy(self, cr, uid, id, default=None, context=None):
raise NotImplementedError(_('The copy method is not implemented on this object !'))
def exists(self, cr, uid, id, context=None):
raise NotImplementedError(_('The exists method is not implemented on this object !'))
def read_string(self, cr, uid, id, langs, fields=None, context=None):
res = {}
res2 = {}
@ -2219,9 +2182,6 @@ class orm_template(object):
self.pool.get(table).write_string(cr, uid, id, langs, vals, context)
return True
def _check_removed_columns(self, cr, log=False):
raise NotImplementedError()
def _add_missing_default_values(self, cr, uid, values, context=None):
missing_defaults = []
avoid_tables = [] # avoid overriding inherited values when parent is set
@ -2251,12 +2211,6 @@ class orm_template(object):
values = defaults
return values
class orm(orm_template):
_sql_constraints = []
_table = None
_protected = ['read', 'write', 'create', 'default_get', 'perm_read', 'unlink', 'fields_get', 'fields_view_get', 'search', 'name_get', 'distinct_field_get', 'name_search', 'copy', 'import_data', 'search_count', 'exists']
__logger = logging.getLogger('orm')
__schema = logging.getLogger('orm.schema')
def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False):
"""
Get the list of records in list view grouped by the given ``groupby`` fields
@ -2972,109 +2926,11 @@ class orm(orm_template):
cr.execute(line2)
cr.commit()
@classmethod
def createInstance(cls, pool, cr):
return cls.makeInstance(pool, cr, ['_columns', '_defaults',
'_inherits', '_constraints', '_sql_constraints'])
def __init__(self, pool, cr):
"""
- copy the stored fields' functions in the osv_pool,
- update the _columns with the fields found in ir_model_fields,
- ensure there is a many2one for each _inherits'd parent,
- update the children's _columns,
- give a chance to each field to initialize itself.
"""
super(orm, self).__init__(pool, cr)
if not hasattr(self, '_log_access'):
# if not access is not specify, it is the same value as _auto
self._log_access = getattr(self, "_auto", True)
self._columns = self._columns.copy()
for store_field in self._columns:
f = self._columns[store_field]
if hasattr(f, 'digits_change'):
f.digits_change(cr)
if not isinstance(f, fields.function):
continue
if not f.store:
continue
if self._columns[store_field].store is True:
sm = {self._name: (lambda self, cr, uid, ids, c={}: ids, None, 10, None)}
else:
sm = self._columns[store_field].store
for object, aa in sm.items():
if len(aa) == 4:
(fnct, fields2, order, length) = aa
elif len(aa) == 3:
(fnct, fields2, order) = aa
length = None
else:
raise except_orm('Error',
('Invalid function definition %s in object %s !\nYou must use the definition: store={object:(fnct, fields, priority, time length)}.' % (store_field, self._name)))
self.pool._store_function.setdefault(object, [])
ok = True
for x, y, z, e, f, l in self.pool._store_function[object]:
if (x==self._name) and (y==store_field) and (e==fields2):
if f == order:
ok = False
if ok:
self.pool._store_function[object].append( (self._name, store_field, fnct, fields2, order, length))
self.pool._store_function[object].sort(lambda x, y: cmp(x[4], y[4]))
for (key, _, msg) in self._sql_constraints:
self.pool._sql_error[self._table+'_'+key] = msg
# Load manual fields
cr.execute("SELECT id FROM ir_model_fields WHERE name=%s AND model=%s", ('state', 'ir.model.fields'))
if cr.fetchone():
cr.execute('SELECT * FROM ir_model_fields WHERE model=%s AND state=%s', (self._name, 'manual'))
for field in cr.dictfetchall():
if field['name'] in self._columns:
continue
attrs = {
'string': field['field_description'],
'required': bool(field['required']),
'readonly': bool(field['readonly']),
'domain': eval(field['domain']) if field['domain'] else None,
'size': field['size'],
'ondelete': field['on_delete'],
'translate': (field['translate']),
'manual': True,
#'select': int(field['select_level'])
}
if field['ttype'] == 'selection':
self._columns[field['name']] = fields.selection(eval(field['selection']), **attrs)
elif field['ttype'] == 'reference':
self._columns[field['name']] = fields.reference(selection=eval(field['selection']), **attrs)
elif field['ttype'] == 'many2one':
self._columns[field['name']] = fields.many2one(field['relation'], **attrs)
elif field['ttype'] == 'one2many':
self._columns[field['name']] = fields.one2many(field['relation'], field['relation_field'], **attrs)
elif field['ttype'] == 'many2many':
_rel1 = field['relation'].replace('.', '_')
_rel2 = field['model'].replace('.', '_')
_rel_name = 'x_%s_%s_%s_rel' % (_rel1, _rel2, field['name'])
self._columns[field['name']] = fields.many2many(field['relation'], _rel_name, 'id1', 'id2', **attrs)
else:
self._columns[field['name']] = getattr(fields, field['ttype'])(**attrs)
self._inherits_check()
self._inherits_reload()
if not self._sequence:
self._sequence = self._table + '_id_seq'
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,)
for f in self._columns:
self._columns[f].restart()
__init__.__doc__ = orm_template.__init__.__doc__ + __init__.__doc__
#
# Update objects that uses this one to update their _inherits fields
#
@ -3153,9 +3009,12 @@ class orm(orm_template):
# return _proxy
def fields_get(self, cr, user, fields=None, context=None, write_access=True):
"""
Get the description of list of fields
def fields_get(self, cr, user, allfields=None, context=None, write_access=True):
""" Return the definition of each field.
The returned value is a dictionary (indiced by field name) of
dictionaries. The _inherits'd fields are included. The string, help,
and selection (if present) attributes are translated.
:param cr: database cursor
:param user: current user id
@ -3165,12 +3024,74 @@ class orm(orm_template):
:raise AccessError: * if user has no create/write rights on the requested object
"""
if context is None:
context = {}
ira = self.pool.get('ir.model.access')
write_access = ira.check(cr, user, self._name, 'write', raise_exception=False, context=context) or \
ira.check(cr, user, self._name, 'create', raise_exception=False, context=context)
return super(orm, self).fields_get(cr, user, fields, context, write_access)
res = {}
translation_obj = self.pool.get('ir.translation')
for parent in self._inherits:
res.update(self.pool.get(parent).fields_get(cr, user, allfields, context))
for f, field in self._columns.iteritems():
if allfields and f not in allfields:
continue
res[f] = fields.field_to_dict(self, cr, user, context, field)
if not write_access:
res[f]['readonly'] = True
res[f]['states'] = {}
if 'string' in res[f]:
res_trans = translation_obj._get_source(cr, user, self._name + ',' + f, 'field', context.get('lang', False) or 'en_US')
if res_trans:
res[f]['string'] = res_trans
if 'help' in res[f]:
help_trans = translation_obj._get_source(cr, user, self._name + ',' + f, 'help', context.get('lang', False) or 'en_US')
if help_trans:
res[f]['help'] = help_trans
if 'selection' in res[f]:
if isinstance(field.selection, (tuple, list)):
sel = field.selection
sel2 = []
for key, val in sel:
val2 = None
if val:
val2 = translation_obj._get_source(cr, user, self._name + ',' + f, 'selection', context.get('lang', False) or 'en_US', val)
sel2.append((key, val2 or val))
res[f]['selection'] = sel2
return res
def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'):
""" Read records with given ids with the given fields
:param cr: database cursor
:param user: current user id
:param ids: id or list of the ids of the records to read
:param fields: optional list of field names to return (default: all fields would be returned)
:type fields: list (example ['field_name_1', ...])
:param context: optional context dictionary - it may contains keys for specifying certain options
like ``context_lang``, ``context_tz`` to alter the results of the call.
A special ``bin_size`` boolean flag may also be passed in the context to request the
value of all fields.binary columns to be returned as the size of the binary instead of its
contents. This can also be selectively overriden by passing a field-specific flag
in the form ``bin_size_XXX: True/False`` where ``XXX`` is the name of the field.
Note: The ``bin_size_XXX`` form is new in OpenERP v6.0.
:return: list of dictionaries((dictionary per record asked)) with requested field values
:rtype: [{name_of_the_field: value, ...}, ...]
:raise AccessError: * if user has no read rights on the requested object
* if user tries to bypass access rules for read on the requested object
"""
if not context:
context = {}
self.pool.get('ir.model.access').check(cr, user, self._name, 'read', context=context)
@ -3959,6 +3880,27 @@ class orm(orm_template):
wf_service.trg_create(user, self._name, id_new, cr)
return id_new
def browse(self, cr, uid, select, context=None, list_class=None, fields_process=None):
"""Fetch records as objects allowing to use dot notation to browse fields and relations
:param cr: database cursor
:param user: current user id
:param select: id or list of ids.
:param context: context arguments, like lang, time zone
:rtype: object or list of objects requested
"""
self._list_class = list_class or browse_record_list
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 browse_record(cr, uid, select, self, cache, context=context, list_class=self._list_class, fields_process=fields_process)
elif isinstance(select, list):
return self._list_class([browse_record(cr, uid, id, self, cache, context=context, list_class=self._list_class, fields_process=fields_process) for id in select], context=context)
else:
return browse_null()
def _store_get_values(self, cr, uid, ids, fields, context):
"""Returns an ordered list of fields.functions to call due to
an update operation on ``fields`` of records with ``ids``,