[MERGE] improvements/fixes of yaml_import for a more realistic creation of record (as if it was done by a form view)

* Upon creation of records in yaml, the view to use for the one2many fields should be the one define in-line in the record form view (if any)
 * Now raises an error if an onchange call returns a field that is not in the view (because computing something that is of no use is probably not expected: the onchange OR the view should be fixed/improved)
 * Improve performances by reducing the number of fields_view_get()  and fields_get() call
 * code refactoring

bzr revid: qdp-launchpad@openerp.com-20121005130226-0etkh88cq2puukrf
This commit is contained in:
Quentin (OpenERP) 2012-10-05 15:02:26 +02:00
commit 47275f238d
2 changed files with 79 additions and 65 deletions

View File

@ -301,7 +301,7 @@ class res_partner(osv.osv, format_address):
def onchange_type(self, cr, uid, ids, is_company, context=None):
# get value as for an onchange on the image
value = tools.image_get_resized_images(self._get_default_image(cr, uid, is_company, context), return_big=True)
value = tools.image_get_resized_images(self._get_default_image(cr, uid, is_company, context), return_big=True, return_medium=False, return_small=False)
value['title'] = False
if is_company:
value['parent_id'] = False

View File

@ -313,13 +313,13 @@ class YamlInterpreter(object):
#context = self.get_context(record, self.eval_context)
#TOFIX: record.context like {'withoutemployee':True} should pass from self.eval_context. example: test_project.yml in project module
context = record.context
view_info = False
if view_id:
varg = view_id
if view_id is True: varg = False
view = model.fields_view_get(self.cr, SUPERUSER_ID, varg, 'form', context)
view_id = etree.fromstring(view['arch'].encode('utf-8'))
view_info = model.fields_view_get(self.cr, SUPERUSER_ID, varg, 'form', context)
record_dict = self._create_record(model, fields, view_id, default=default)
record_dict = self._create_record(model, fields, view_info, default=default)
_logger.debug("RECORD_DICT %s" % record_dict)
id = self.pool.get('ir.model.data')._update(self.cr, SUPERUSER_ID, record.model, \
self.module, record_dict, record.id, noupdate=self.isnoupdate(record), mode=self.mode, context=context)
@ -327,16 +327,18 @@ class YamlInterpreter(object):
if config.get('import_partial'):
self.cr.commit()
def _create_record(self, model, fields, view=False, parent={}, default=True):
if view is not False:
defaults = default and model._add_missing_default_values(self.cr, SUPERUSER_ID, {}, context=self.context) or {}
fg = model.fields_get(self.cr, SUPERUSER_ID, context=self.context)
else:
defaults = {}
fg = {}
record_dict = {}
fields = fields or {}
def _create_record(self, model, fields, view_info=False, parent={}, default=True):
"""This function processes the !record tag in yalm files. It simulates the record creation through an xml
view (either specified on the !record tag or the default one for this object), including the calls to
on_change() functions.
:param model: model instance
:param fields: dictonary mapping the field names and their values
:param view_info: result of fields_view_get() called on the object
:param parent: dictionary containing the values already computed for the parent, in case of one2many fields
:param default: if True, the default values must be processed too or not
:return: dictionary mapping the field names and their values, ready to use when calling the create() function
:rtype: dict
"""
def process_val(key, val):
if fg[key]['type']=='many2one':
if type(val) in (tuple,list):
@ -348,63 +350,75 @@ class YamlInterpreter(object):
val = map(lambda x: (0,0,x), val)
return val
# Process all on_change calls
nodes = (view is not False) and [view] or []
while nodes:
el = nodes.pop(0)
if el.tag=='field':
field_name = el.attrib['name']
assert field_name in fg, "The field '%s' is defined in the form view but not on the object '%s'!" % (field_name, model._name)
if field_name in fields:
view2 = None
# if the form view is not inline, we call fields_view_get
if (view is not False) and (fg[field_name]['type']=='one2many'):
view2 = view.find("field[@name='%s']/form"%(field_name,))
if not view2:
view2 = self.pool.get(fg[field_name]['relation']).fields_view_get(self.cr, SUPERUSER_ID, False, 'form', self.context)
view2 = etree.fromstring(view2['arch'].encode('utf-8'))
view = view_info and etree.fromstring(view_info['arch'].encode('utf-8')) or False
fields = fields or {}
if view is not False:
fg = view_info['fields']
# gather the default values on the object. (Can't use `fields´ as parameter instead of {} because we may
# have references like `base.main_company´ in the yaml file and it's not compatible with the function)
defaults = default and model._add_missing_default_values(self.cr, SUPERUSER_ID, {}, context=self.context) or {}
field_value = self._eval_field(model, field_name, fields[field_name], view2, parent=record_dict, default=default)
record_dict[field_name] = field_value
#if (field_name in defaults) and defaults[field_name] == field_value:
# print '*** You can remove these lines:', field_name, field_value
elif (field_name in defaults):
if (field_name not in record_dict):
record_dict[field_name] = process_val(field_name, defaults[field_name])
else:
continue
# copy the default values in record_dict, only if they are in the view (because that's what the client does)
# the other default values will be added later on by the create().
record_dict = dict([(key, val) for key, val in defaults.items() if key in fg])
if not el.attrib.get('on_change', False):
continue
match = re.match("([a-z_1-9A-Z]+)\((.*)\)", el.attrib['on_change'])
assert match, "Unable to parse the on_change '%s'!" % (el.attrib['on_change'], )
# Process all on_change calls
nodes = [view]
while nodes:
el = nodes.pop(0)
if el.tag=='field':
field_name = el.attrib['name']
assert field_name in fg, "The field '%s' is defined in the form view but not on the object '%s'!" % (field_name, model._name)
if field_name in fields:
one2many_form_view = None
if (view is not False) and (fg[field_name]['type']=='one2many'):
# for one2many fields, we want to eval them using the inline form view defined on the parent
one2many_form_view = view_info['fields'][field_name]['views'].get('form')
# if the form view is not defined inline, we call fields_view_get()
if not one2many_form_view:
one2many_form_view = self.pool.get(fg[field_name]['relation']).fields_view_get(self.cr, SUPERUSER_ID, False, 'form', self.context)
# creating the context
class parent2(object):
def __init__(self, d):
self.d = d
def __getattr__(self, name):
return self.d.get(name, False)
field_value = self._eval_field(model, field_name, fields[field_name], one2many_form_view or view_info, parent=record_dict, default=default)
record_dict[field_name] = field_value
#if (field_name in defaults) and defaults[field_name] == field_value:
# print '*** You can remove these lines:', field_name, field_value
ctx = record_dict.copy()
ctx['context'] = self.context
ctx['uid'] = 1
ctx['parent'] = parent2(parent)
for a in fg:
if a not in ctx:
ctx[a]=process_val(a, defaults.get(a, False))
#if field_name has a default value or a value is given in the yaml file, we must call its on_change()
elif field_name not in defaults:
continue
# Evaluation args
args = map(lambda x: eval(x, ctx), match.group(2).split(','))
result = getattr(model, match.group(1))(self.cr, SUPERUSER_ID, [], *args)
for key, val in (result or {}).get('value', {}).items():
if key not in fields:
assert key in fg, "The returning field '%s' from your on_change call '%s' does not exist on the object '%s'" % (key, match.group(1), model._name)
if not el.attrib.get('on_change', False):
continue
match = re.match("([a-z_1-9A-Z]+)\((.*)\)", el.attrib['on_change'])
assert match, "Unable to parse the on_change '%s'!" % (el.attrib['on_change'], )
# creating the context
class parent2(object):
def __init__(self, d):
self.d = d
def __getattr__(self, name):
return self.d.get(name, False)
ctx = record_dict.copy()
ctx['context'] = self.context
ctx['uid'] = SUPERUSER_ID
ctx['parent'] = parent2(parent)
for a in fg:
if a not in ctx:
ctx[a]=process_val(a, defaults.get(a, False))
# Evaluation args
args = map(lambda x: eval(x, ctx), match.group(2).split(','))
result = getattr(model, match.group(1))(self.cr, SUPERUSER_ID, [], *args)
for key, val in (result or {}).get('value', {}).items():
assert key in fg, "The returning field '%s' from your on_change call '%s' does not exist either on the object '%s', either in the view '%s' used for the creation" % (key, match.group(1), model._name, view_info['name'])
record_dict[key] = process_val(key, val)
#if (key in fields) and record_dict[key] == process_val(key, val):
# print '*** You can remove these lines:', key, val
else:
nodes = list(el) + nodes
else:
nodes = list(el) + nodes
else:
record_dict = {}
for field_name, expression in fields.items():
if field_name in record_dict:
@ -440,7 +454,7 @@ class YamlInterpreter(object):
def process_eval(self, node):
return eval(node.expression, self.eval_context)
def _eval_field(self, model, field_name, expression, view=False, parent={}, default=True):
def _eval_field(self, model, field_name, expression, view_info=False, parent={}, default=True):
# TODO this should be refactored as something like model.get_field() in bin/osv
if field_name in model._columns:
column = model._columns[field_name]
@ -461,7 +475,7 @@ class YamlInterpreter(object):
value = self.get_id(expression)
elif column._type == "one2many":
other_model = self.get_model(column._obj)
value = [(0, 0, self._create_record(other_model, fields, view, parent, default=default)) for fields in expression]
value = [(0, 0, self._create_record(other_model, fields, view_info, parent, default=default)) for fields in expression]
elif column._type == "many2many":
ids = [self.get_id(xml_id) for xml_id in expression]
value = [(6, 0, ids)]