[MERGE] some refactoring in fields_view_get.

bzr revid: vmt@openerp.com-20110627132601-qw6ejx2v4xywq4ai
This commit is contained in:
Vo Minh Thu 2011-06-27 15:26:01 +02:00
commit 027d082936
2 changed files with 156 additions and 99 deletions

View File

@ -1178,5 +1178,24 @@ def field_to_dict(self, cr, user, context, field):
return res
class column_info(object):
"""Struct containing details about an osv column, either one local to
its model, or one inherited via _inherits.
:attr name: name of the column
:attr column: column instance, subclass of osv.fields._column
:attr parent_model: if the column is inherited, name of the model
that contains it, None for local columns.
:attr parent_column: the name of the column containing the m2o
relationship to the parent model that contains
this column, None for local columns.
"""
def __init__(self, name, column, parent_model=None, parent_column=None):
self.name = name
self.column = column
self.parent_model = parent_model
self.parent_column = parent_column
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -479,6 +479,11 @@ class orm_template(object):
# r is the (local) field towards m,
# and f is the _column object itself.
_inherit_fields = {}
# Mapping field name/column_info object
# This is similar to _inherit_fields but:
# 1. includes self fields,
# 2. uses column_info instead of a triple.
_all_columns = {}
_table = None
_invalids = set()
_log_create = False
@ -1265,7 +1270,7 @@ class orm_template(object):
def fields_get(self, cr, user, allfields=None, context=None, write_access=True):
""" Returns the definition of each field.
""" 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,
@ -1320,7 +1325,7 @@ class orm_template(object):
return False
def __view_look_dom(self, cr, user, node, view_id, context=None):
if not context:
if context is None:
context = {}
result = False
fields = {}
@ -1331,8 +1336,8 @@ class orm_template(object):
return s.encode('utf8')
return s
# return True if node can be displayed to current user
def check_group(node):
""" Set invisible to true if the user is not in the specified groups. """
if node.get('groups'):
groups = node.get('groups').split(',')
access_pool = self.pool.get('ir.model.access')
@ -1342,9 +1347,6 @@ class orm_template(object):
if 'attrs' in node.attrib:
del(node.attrib['attrs']) #avoid making field visible later
del(node.attrib['groups'])
return can_see
else:
return True
if node.tag in ('field', 'node', 'arrow'):
if node.get('object'):
@ -1423,12 +1425,11 @@ class orm_template(object):
if node.get(additional_field):
fields[node.get(additional_field)] = {}
if 'groups' in node.attrib:
check_group(node)
check_group(node)
# translate view
if ('lang' in context) and not result:
if node.get('string'):
if 'lang' in context:
if node.get('string') and not result:
trans = self.pool.get('ir.translation')._get_source(cr, user, self._name, 'view', context['lang'], node.get('string'))
if trans == node.get('string') and ('base_model_name' in context):
# If translation is same as source, perhaps we'd have more luck with the alternative model name
@ -1456,6 +1457,7 @@ class orm_template(object):
return fields
def _disable_workflow_buttons(self, cr, user, node):
""" Set the buttons in node to readonly if the user can't activate them. """
if user == 1:
# admin user can always activate workflow buttons
return node
@ -1487,12 +1489,10 @@ class orm_template(object):
if node.tag == 'diagram':
if node.getchildren()[0].tag == 'node':
node_fields = self.pool.get(node.getchildren()[0].get('object')).fields_get(cr, user, fields_def.keys(), context)
fields.update(node_fields)
if node.getchildren()[1].tag == 'arrow':
arrow_fields = self.pool.get(node.getchildren()[1].get('object')).fields_get(cr, user, fields_def.keys(), context)
for key, value in node_fields.items():
fields[key] = value
for key, value in arrow_fields.items():
fields[key] = value
fields.update(arrow_fields)
else:
fields = self.fields_get(cr, user, fields_def.keys(), context)
for field in fields_def:
@ -1563,6 +1563,7 @@ class orm_template(object):
tree_view = self.fields_view_get(cr, uid, False, 'tree', context=context)
fields_to_search = set()
# TODO it seems _all_columns could be used instead of fields_get (no need for translated fields info)
fields = self.fields_get(cr, uid, context=context)
for field in fields:
if fields[field].get('select'):
@ -1617,63 +1618,79 @@ class orm_template(object):
raise AttributeError("View definition error for inherited view '%s' on model '%s': %s"
% (child_view.xml_id, self._name, error_msg))
def _inherit_apply(src, inherit, inherit_id=None):
def _find(node, node2):
if node2.tag == 'xpath':
res = node.xpath(node2.get('expr'))
if res:
return res[0]
else:
return None
else:
for n in node.getiterator(node2.tag):
res = True
if node2.tag == 'field':
# only compare field names, a field can be only once in a given view
# at a given level (and for multilevel expressions, we should use xpath
# inheritance spec anyway)
if node2.get('name') == n.get('name'):
return n
else:
continue
for attr in node2.attrib:
if attr == 'position':
continue
if n.get(attr):
if n.get(attr) == node2.get(attr):
continue
res = False
if res:
return n
def locate(source, spec):
""" Locate a node in a source (parent) architecture.
Given a complete source (parent) architecture (i.e. the field
`arch` in a view), and a 'spec' node (a node in an inheriting
view that specifies the location in the source view of what
should be changed), return (if it exists) the node in the
source view matching the specification.
:param source: a parent architecture to modify
:param spec: a modifying node in an inheriting view
:return: a node in the source matching the spec
"""
if spec.tag == 'xpath':
nodes = source.xpath(spec.get('expr'))
return nodes[0] if nodes else None
elif spec.tag == 'field':
# Only compare the field name: a field can be only once in a given view
# at a given level (and for multilevel expressions, we should use xpath
# inheritance spec anyway).
for node in source.getiterator('field'):
if node.get('name') == spec.get('name'):
return node
return None
else:
for node in source.getiterator(spec.tag):
good = True
for attr in spec.attrib:
if attr != 'position' and (not node.get(attr) or node.get(attr) != spec.get(attr)):
good = False
break
if good:
return node
return None
# End: _find(node, node2)
def apply_inheritance_specs(source, specs_arch, inherit_id=None):
""" Apply an inheriting view.
doc_dest = etree.fromstring(encode(inherit))
toparse = [doc_dest]
Apply to a source architecture all the spec nodes (i.e. nodes
describing where and what changes to apply to some parent
architecture) given by an inheriting view.
while len(toparse):
node2 = toparse.pop(0)
if isinstance(node2, SKIPPED_ELEMENT_TYPES):
:param source: a parent architecture to modify
:param specs_arch: a modifying architecture in an inheriting view
:param inherit_id: the database id of the inheriting view
:return: a modified source where the specs are applied
"""
specs_tree = etree.fromstring(encode(specs_arch))
# Queue of specification nodes (i.e. nodes describing where and
# changes to apply to some parent architecture).
specs = [specs_tree]
while len(specs):
spec = specs.pop(0)
if isinstance(spec, SKIPPED_ELEMENT_TYPES):
continue
if node2.tag == 'data':
toparse += [ c for c in doc_dest ]
if spec.tag == 'data':
specs += [ c for c in specs_tree ]
continue
node = _find(src, node2)
node = locate(source, spec)
if node is not None:
pos = 'inside'
if node2.get('position'):
pos = node2.get('position')
pos = spec.get('position', 'inside')
if pos == 'replace':
parent = node.getparent()
if parent is None:
src = copy.deepcopy(node2[0])
if node.getparent() is None:
source = copy.deepcopy(spec[0])
else:
for child in node2:
for child in spec:
node.addprevious(child)
node.getparent().remove(node)
elif pos == 'attributes':
for child in node2.getiterator('attribute'):
for child in spec.getiterator('attribute'):
attribute = (child.get('name'), child.text and child.text.encode('utf8') or None)
if attribute[1]:
node.set(attribute[0], attribute[1])
@ -1681,7 +1698,7 @@ class orm_template(object):
del(node.attrib[attribute[0]])
else:
sib = node.getnext()
for child in node2:
for child in spec:
if pos == 'inside':
node.append(child)
elif pos == 'after':
@ -1696,22 +1713,39 @@ class orm_template(object):
raise_view_error("Invalid position value: '%s'" % pos, inherit_id)
else:
attrs = ''.join([
' %s="%s"' % (attr, node2.get(attr))
for attr in node2.attrib
' %s="%s"' % (attr, spec.get(attr))
for attr in spec.attrib
if attr != 'position'
])
tag = "<%s%s>" % (node2.tag, attrs)
tag = "<%s%s>" % (spec.tag, attrs)
raise_view_error("Element '%s' not found in parent view '%%(parent_xml_id)s'" % tag, inherit_id)
return src
# End: _inherit_apply(src, inherit)
return source
def apply_view_inheritance(source, inherit_id):
""" Apply all the (directly and indirectly) inheriting views.
:param source: a parent architecture to modify (with parent
modifications already applied)
:param inherit_id: the database id of the parent view
:return: a modified source where all the modifying architecture
are applied
"""
# get all views which inherit from (ie modify) this view
cr.execute('select arch,id from ir_ui_view where inherit_id=%s and model=%s order by priority', (inherit_id, self._name))
sql_inherit = cr.fetchall()
for (inherit, id) in sql_inherit:
source = apply_inheritance_specs(source, inherit, id)
source = apply_view_inheritance(source, id)
return source
result = {'type': view_type, 'model': self._name}
ok = True
sql_res = False
parent_view_model = None
while ok:
view_ref = context.get(view_type + '_view_ref', False)
view_ref = context.get(view_type + '_view_ref')
# Search for a root (i.e. without any parent) view.
while True:
if view_ref and not view_id:
if '.' in view_ref:
module, view_ref = view_ref.split('.', 1)
@ -1725,48 +1759,35 @@ class orm_template(object):
FROM ir_ui_view
WHERE id=%s""", (view_id,))
else:
cr.execute('''SELECT
arch,name,field_parent,id,type,inherit_id,model
FROM
ir_ui_view
WHERE
model=%s AND
type=%s AND
inherit_id IS NULL
ORDER BY priority''', (self._name, view_type))
sql_res = cr.fetchone()
cr.execute("""SELECT arch,name,field_parent,id,type,inherit_id,model
FROM ir_ui_view
WHERE model=%s AND type=%s AND inherit_id IS NULL
ORDER BY priority""", (self._name, view_type))
sql_res = cr.dictfetchone()
if not sql_res:
break
ok = sql_res[5]
view_id = ok or sql_res[3]
parent_view_model = sql_res[6]
view_id = sql_res['inherit_id'] or sql_res['id']
parent_view_model = sql_res['model']
if not sql_res['inherit_id']:
break
# if a view was found
if sql_res:
result['type'] = sql_res[4]
result['view_id'] = sql_res[3]
result['arch'] = sql_res[0]
result['type'] = sql_res['type']
result['view_id'] = sql_res['id']
def _inherit_apply_rec(result, inherit_id):
# get all views which inherit from (ie modify) this view
cr.execute('select arch,id from ir_ui_view where inherit_id=%s and model=%s order by priority', (inherit_id, self._name))
sql_inherit = cr.fetchall()
for (inherit, id) in sql_inherit:
result = _inherit_apply(result, inherit, id)
result = _inherit_apply_rec(result, id)
return result
source = etree.fromstring(encode(sql_res['arch']))
result['arch'] = apply_view_inheritance(source, result['view_id'])
inherit_result = etree.fromstring(encode(result['arch']))
result['arch'] = _inherit_apply_rec(inherit_result, sql_res[3])
result['name'] = sql_res[1]
result['field_parent'] = sql_res[2] or False
result['name'] = sql_res['name']
result['field_parent'] = sql_res['field_parent'] or False
else:
# otherwise, build some kind of default view
if view_type == 'form':
# TODO it seems fields_get can be replaced by _all_columns (no need for translation)
res = self.fields_get(cr, user, context=context)
xml = '<?xml version="1.0" encoding="utf-8"?> ' \
'<form string="%s">' % (self._description,)
@ -1792,7 +1813,7 @@ class orm_template(object):
xml = self.__get_default_search_view(cr, user, context)
else:
xml = '<?xml version="1.0"?>' # what happens here, graph case?
# what happens here, graph case?
raise except_orm(_('Invalid Architecture!'), _("There is no view of type '%s' defined for the structure!") % view_type)
result['arch'] = etree.fromstring(encode(xml))
result['name'] = 'default'
@ -2352,6 +2373,7 @@ class orm(orm_template):
groupby_def = self._columns.get(groupby) or (self._inherit_fields.get(groupby) and self._inherit_fields.get(groupby)[2])
assert groupby_def and groupby_def._classic_write, "Fields in 'groupby' must be regular database-persisted fields (no function or related fields), or function fields with store=True"
# TODO it seems fields_get can be replaced by _all_columns (no need for translation)
fget = self.fields_get(cr, uid, fields)
float_int_fields = filter(lambda x: fget[x]['type'] in ('float', 'integer'), fields)
flist = ''
@ -3132,6 +3154,7 @@ class orm(orm_template):
if self._name in obj._inherits:
obj._inherits_reload()
def _inherits_reload(self):
""" Recompute the _inherit_fields mapping.
@ -3141,14 +3164,27 @@ class orm(orm_template):
res = {}
for table in self._inherits:
other = self.pool.get(table)
res.update(other._inherit_fields)
for col in other._columns.keys():
res[col] = (table, self._inherits[table], other._columns[col])
for col in other._inherit_fields.keys():
res[col] = (table, self._inherits[table], other._inherit_fields[col][2])
self._inherit_fields = res
self._all_columns = self._get_column_infos()
self._inherits_reload_src()
def _get_column_infos(self):
"""Returns a dict mapping all fields names (direct fields and
inherited field via _inherits) to a ``column_info`` struct
giving detailed columns """
result = {}
for k, (parent, m2o, col) in self._inherit_fields.iteritems():
result[k] = fields.column_info(k, col, parent, m2o)
for k, col in self._columns.iteritems():
result[k] = fields.column_info(k, col)
return result
def _inherits_check(self):
for table, field_name in self._inherits.items():
if field_name not in self._columns:
@ -4365,6 +4401,7 @@ class orm(orm_template):
else:
raise IndexError( _("Record #%d of %s not found, cannot copy!") %( id, self._name))
# TODO it seems fields_get can be replaced by _all_columns (no need for translation)
fields = self.fields_get(cr, uid, context=context)
for f in fields:
ftype = fields[f]['type']
@ -4422,6 +4459,7 @@ class orm(orm_template):
seen_map[self._name].append(old_id)
trans_obj = self.pool.get('ir.translation')
# TODO it seems fields_get can be replaced by _all_columns (no need for translation)
fields = self.fields_get(cr, uid, context=context)
translation_records = []