untangle inheritance and postprocessing

bzr revid: al@openerp.com-20130629213516-pqcl6oz1g3o5tpgn
This commit is contained in:
Antony Lesuisse 2013-06-29 23:35:16 +02:00
parent ca98a61a3d
commit 932efea388
2 changed files with 324 additions and 447 deletions

View File

@ -57,19 +57,6 @@ class view_custom(osv.osv):
class view(osv.osv):
_name = 'ir.ui.view'
class NoViewError(Exception): pass
class NoDefaultError(NoViewError): pass
def _type_field(self, cr, uid, ids, name, args, context=None):
result = {}
for record in self.browse(cr, uid, ids, context):
# Get the type from the inherited view if any.
if record.inherit_id:
result[record.id] = record.inherit_id.type
else:
result[record.id] = etree.fromstring(record.arch.encode('utf8')).tag
return result
def _arch_get(self, cr, uid, ids, name, arg, context=None):
"""
For each id being read, return arch_db or the content of arch_file
@ -130,17 +117,6 @@ class view(osv.osv):
# Holds the RNG schema
_relaxng_validator = None
def create(self, cr, uid, values, context=None):
if 'type' not in values:
if values.get('inherit_id'):
values['type'] = self.browse(cr, uid, values['inherit_id'], context).type
else:
values['type'] = etree.fromstring(values['arch']).tag
if not values.get('name'):
values['name'] = "%s %s" % (values['model'], values['type'])
return super(view, self).create(cr, uid, values, context)
def _relaxng(self):
if not self._relaxng_validator:
frng = tools.file_open(os.path.join('base','rng','view.rng'))
@ -157,7 +133,7 @@ class view(osv.osv):
for view in self.browse(cr, uid, ids, context):
# Sanity check: the view should not break anything upon rendering!
try:
fvg = self.read_combined(cr, uid, view.id, view.type, view.model, context=context)
fvg = self.read_combined(cr, uid, view.id, None, context=context)
view_arch_utf8 = fvg['arch']
except Exception, e:
_logger.exception(e)
@ -179,15 +155,8 @@ class view(osv.osv):
return False
return True
def _check_model(self, cr, uid, ids, context=None):
for view in self.browse(cr, uid, ids, context):
if view.model and view.model not in self.pool:
return False
return True
_constraints = [
(_check_xml, 'Invalid XML for View Architecture!', ['arch'])
#(_check_model, 'The model name does not exist.', ['model']),
]
def _auto_init(self, cr, context=None):
@ -196,107 +165,85 @@ class view(osv.osv):
if not cr.fetchone():
cr.execute('CREATE INDEX ir_ui_view_model_type_inherit_id ON ir_ui_view (model, inherit_id)')
def read_combined(self, cr, uid, view_id, view_type, model,
fields=None, fallback=None, context=None):
def create(self, cr, uid, values, context=None):
if 'type' not in values:
if values.get('inherit_id'):
values['type'] = self.browse(cr, uid, values['inherit_id'], context).type
else:
values['type'] = etree.fromstring(values['arch']).tag
if not values.get('name'):
values['name'] = "%s %s" % (values['model'], values['type'])
return super(view, self).create(cr, uid, values, context)
def write(self, cr, uid, ids, vals, context=None):
if not isinstance(ids, (list, tuple)):
ids = [ids]
# drop the corresponding view customizations (used for dashboards for example), otherwise
# not all users would see the updated views
custom_view_ids = self.pool.get('ir.ui.view.custom').search(cr, uid, [('ref_id','in',ids)])
if custom_view_ids:
self.pool.get('ir.ui.view.custom').unlink(cr, uid, custom_view_ids)
return super(view, self).write(cr, uid, ids, vals, context)
# default view selection
def default_view(self, cr, uid, model, view_type, context=None):
""" Fetches the default view for the provided (model, view_type) pair:
view with no parent (inherit_id=Fase) with the lowest priority.
:param str model:
:param int view_type:
:return: id of the default view of False if none found
:rtype: int
"""
Utility function stringing together all method calls necessary to get
a full, final view:
ids = self.search(cr, uid, [
['model', '=', model],
['type', '=', view_type],
['inherit_id', '=', False],
], limit=1, order='priority', context=context)
if not ids:
return False
return ids[0]
* Gets the default view if no view_id is provided
* Gets the top of the view tree if a sub-view is requested
* Applies all inherited archs on the root view
* Applies post-processing
* Returns the view with all requested fields
# inheritance
.. note:: ``arch`` is always added to the fields list even if not
requested (similar to ``id``)
def get_inheriting_views_arch(self, cr, uid, view_id, context=None):
"""Retrieves the architecture of views that inherit from the given view, from the sets of
views that should currently be used in the system. During the module upgrade phase it
may happen that a view is present in the database but the fields it relies on are not
fully loaded yet. This method only considers views that belong to modules whose code
is already loaded. Custom views defined directly in the database are loaded only
after the module initialization phase is completely finished.
If no view is available (no view_id or invalid view_id provided, or
no view stored for (model, view_type)) a view record will be fetched
from the ``defaults`` mapping?
:param fallback: a mapping of {view_type: view_dict}, if no view can
be found (read) will be used to provide a default
before post-processing
:type fallback: mapping
:param int view_id: id of the view whose inheriting views should be retrieved
:param str model: model identifier of the view's related model (for double-checking)
:rtype: list of tuples
:return: [(view_arch,view_id), ...]
"""
if context is None: context = {}
user_groups = frozenset(self.pool.get('res.users').browse(cr, 1, uid, context).groups_id)
def clean_anotations(arch, parent_info=None):
for child in arch:
if child.tag == 't' or child.tag == 'field':
# can not anote t and field while cleaning
continue
conditions = [['inherit_id', '=', view_id]]
if self.pool._init:
# Module init currently in progress, only consider views from
# modules whose code is already loaded
conditions.extend([
['model_ids.model', '=', 'ir.ui.view'],
['model_ids.module', 'in', tuple(self.pool._init_modules)],
])
view_ids = self.search(cr, uid, conditions, context=context)
child_text = "".join([etree.tostring(x) for x in child])
if child.attrib.get('data-edit-model'):
if child_text.find('data-edit-model') != -1 or child_text.find('<t ') != -1:
# enclosed tag, move present tag to childs
parent_info = {
'model': child.attrib.get('data-edit-model'),
'view_id': child.attrib.get('data-edit-view-id'),
'xpath': child.attrib.get('data-edit-xpath')+'/'+child.tag,
}
child.attrib.pop('data-edit-model')
child.attrib.pop('data-edit-view-id')
child.attrib.pop('data-edit-xpath')
child = clean_anotations(child, parent_info)
else:
# tagged node, no enclosed, can keep, don't care if had parent_info
continue
# filter views based on user groups
return [(view.arch, view.id)
for view in self.browse(cr, 1, view_ids, context)
if not (view.groups_id and user_groups.isdisjoint(view.groups_id))]
else:
if child_text.find('data-edit-model') != -1 or child_text.find('<t ') != -1:
# has other nodes to clean
if parent_info:
# update xpath
parent_info.update({'xpath': parent_info.get('xpath')+'/'+child.tag})
child = clean_anotations(child, parent_info)
elif parent_info:
# put tag from parent, no enclose, higher
child.attrib.update({
'data-edit-model': parent_info.get('model'),
'data-edit-view-id': parent_info.get('view_id'),
'data-edit-xpath': parent_info.get('xpath'),
})
return arch
try:
if not view_id:
view_id = self.default_view(cr, uid, model, view_type, context=context)
root_id = self.root_ancestor(cr, uid, view_id, context=context)
if fields:
needed_fields = ['arch', 'model']
fields = list(itertools.chain(
[field for field in needed_fields if field not in fields],
fields))
[view] = self.read(cr, uid, [root_id], fields=fields, context=context)
arch_tree = etree.fromstring(
view['arch'].encode('utf-8') if isinstance(view['arch'], unicode)
else view['arch'])
descendants = self.iter(
cr, uid, view['id'], model, exclude_base=True, context=context)
arch = self.apply_inherited_archs(
cr, uid, arch_tree, descendants,
model, view['id'], context=context)
arch = self.add_root_tags(arch, model, view['id'])
except self.NoViewError:
# defaultdict is "empty" until first __getattr__
if fallback is None: raise
view = fallback[view_type]
arch = view['arch']
if isinstance(arch, basestring):
arch = etree.fromstring(
arch.encode('utf-8') if isinstance(arch, unicode) else arch)
arch = clean_anotations(arch)
return dict(view, arch=etree.tostring(arch, encoding='utf-8'))
def raise_view_error(self, cr, uid, view_id, message, context=None):
view = self.browse(cr, uid, [view_id], context)
message = "Inherit error: %s view_id: %s, xml_id: %s, model: %s, parent_view: %s" % (message, view_id, view.xml_id, view.model, view.inherit_id)
raise AttributeError(message)
def locate_node(self, arch, spec):
""" Locate a node in a source (parent) architecture.
@ -335,144 +282,7 @@ class view(osv.osv):
return node
return None
def get_inheriting_views_arch(self, cr, uid, view_id, model, context=None):
"""Retrieves the architecture of views that inherit from the given view, from the sets of
views that should currently be used in the system. During the module upgrade phase it
may happen that a view is present in the database but the fields it relies on are not
fully loaded yet. This method only considers views that belong to modules whose code
is already loaded. Custom views defined directly in the database are loaded only
after the module initialization phase is completely finished.
:param int view_id: id of the view whose inheriting views should be retrieved
:param str model: model identifier of the view's related model (for double-checking)
:rtype: list of tuples
:return: [(view_arch,view_id), ...]
"""
user_groups = frozenset(self.pool.get('res.users').browse(cr, 1, uid, context).groups_id)
conditions = [['inherit_id', '=', view_id], ['model', '=', model]]
if self.pool._init:
# Module init currently in progress, only consider views from
# modules whose code is already loaded
conditions.extend([
['model_ids.model', '=', 'ir.ui.view'],
['model_ids.module', 'in', tuple(self.pool._init_modules)],
])
view_ids = self.search(cr, uid, conditions, context=context)
# filter views based on user groups
return [(view.arch, view.id)
for view in self.browse(cr, 1, view_ids, context)
if not (view.groups_id and user_groups.isdisjoint(view.groups_id))]
def add_root_tags(self, arch_tree, model, view_id):
for child in arch_tree:
if child.tag == 'data' or child.tag == 'xpath':
child = self.add_root_tags(child, model, view_id)
else:
child.attrib.update({
'data-edit-model': model or 'undefined',
'data-edit-view-id': str(view_id),
'data-edit-xpath': '/'
})
return arch_tree
def iter(self, cr, uid, view_id, model, exclude_base=False, context=None):
""" iterates on all of ``view_id``'s descendants tree depth-first.
If ``exclude_base`` is ``False``, also yields ``view_id`` itself. It is
``False`` by default to match the behavior of etree's Element.iter.
:param int view_id: database id of the root view
:param str model: name of the view's related model (for filtering)
:param bool exclude_base: whether ``view_id`` should be excluded
from the iteration
:return: iterator of (database_id, arch_string) pairs for all
descendants of ``view_id`` (including ``view_id`` itself if
``exclude_base`` is ``False``, the default)
"""
if not exclude_base:
base = self.browse(cr, uid, view_id, context=context)
# should we do add_root_tags this one as well ?
yield base.id, base.arch
for arch, id in self.get_inheriting_views_arch(
cr, uid, view_id, model, context=context):
arch = self.add_root_tags(etree.fromstring(arch), model, view_id)
yield id, etree.tostring(arch)
for info in self.iter(
cr, uid, id, model, exclude_base=True, context=None):
yield info
def default_view(self, cr, uid, model, view_type, context=None):
""" Fetches the default view for the provided (model, view_type) pair:
view with no parent (inherit_id=Fase) with the lowest priority.
:param str model:
:param int view_type:
:return: id of the default view for the (model, view_type) pair
:rtype: int
"""
ids = self.search(cr, uid, [
['model', '=', model],
['type', '=', view_type],
['inherit_id', '=', False],
], limit=1, order='priority', context=context)
if not ids:
raise self.NoDefaultError(
_("No default view of type %s for model %s") % (
view_type, model))
return ids[0]
def root_ancestor(self, cr, uid, view_id, context=None):
"""
Fetches the id of the root of the view tree of which view_id is part
If view_id is specified, view_type and model aren't needed (and the
other way around)
:param view_id: id of view to search the root ancestor of
:return: id of the root view for the tree
"""
view = self.browse(cr, uid, view_id, context=context)
if not view.exists():
raise self.NoViewError(
_("No view for id %s, root ancestor not available") % view_id)
# Search for a root (i.e. without any parent) view.
while view.inherit_id:
view = view.inherit_id
return view.id
def apply_inherited_archs(self, cr, uid, source, descendants,
model, source_view_id, context=None):
""" Applies descendants to the ``source`` view, returns the result of
the application.
:param Element source: source arch to apply descendant on
:param descendants: iterable of (id, arch_string) pairs of all
descendants in the view tree, depth-first,
excluding the base view
:type descendants: iter((int, str))
:return: new architecture etree produced by applying all descendants
on ``source``
:rtype: Element
"""
return reduce(
lambda current_arch, descendant: self.apply_inheritance_specs(
cr, uid, model, source_view_id, current_arch,
*descendant, context=context),
descendants, source)
def raise_view_error(self, cr, uid, model, error_msg, view_id, child_view_id, context=None):
view, child_view = self.browse(cr, uid, [view_id, child_view_id], context)
error_msg = error_msg % {'parent_xml_id': view.xml_id}
raise AttributeError("View definition error for inherited view '%s' on model '%s': %s"
% (child_view.xml_id, model, error_msg))
def apply_inheritance_specs(self, cr, uid, model, root_view_id, source, descendant_id, specs_arch, context=None):
def apply_inheritance_specs(self, cr, uid, source, specs_arch, inherit_id, context=None):
""" Apply an inheriting view (a descendant of the base view)
Apply to a source architecture all the spec nodes (i.e. nodes
@ -530,7 +340,7 @@ class view(osv.osv):
elif pos == 'before':
node.addprevious(child)
else:
self.raise_view_error(cr, uid, model, "Invalid position value: '%s'" % pos, root_view_id, descendant_id, context=context)
self.raise_view_error(cr, uid, "Invalid position value: '%s'" % pos, inherit_id, context=context)
else:
attrs = ''.join([
' %s="%s"' % (attr, spec.get(attr))
@ -538,94 +348,69 @@ class view(osv.osv):
if attr != 'position'
])
tag = "<%s%s>" % (spec.tag, attrs)
if spec.get('version') and spec.get('version') != source.get('version'):
self.raise_view_error(cr, uid, model, "Mismatching view API version for element '%s': %r vs %r in parent view '%%(parent_xml_id)s'" % \
(tag, spec.get('version'), source.get('version')), root_view_id, descendant_id, context=context)
self.raise_view_error(cr, uid, model, "Element '%s' not found in parent view '%%(parent_xml_id)s'" % tag, root_view_id, descendant_id, context=context)
self.raise_view_error(cr, uid, "Element '%s' not found in parent view " % tag, inherit_id, context=context)
return source
def write(self, cr, uid, ids, vals, context=None):
if not isinstance(ids, (list, tuple)):
ids = [ids]
# drop the corresponding view customizations (used for dashboards for example), otherwise
# not all users would see the updated views
custom_view_ids = self.pool.get('ir.ui.view.custom').search(cr, uid, [('ref_id','in',ids)])
if custom_view_ids:
self.pool.get('ir.ui.view.custom').unlink(cr, uid, custom_view_ids)
return super(view, self).write(cr, uid, ids, vals, context)
def graph_get(self, cr, uid, id, model, node_obj, conn_obj, src_node, des_node, label, scale, context=None):
nodes=[]
nodes_name=[]
transitions=[]
start=[]
tres={}
labels={}
no_ancester=[]
blank_nodes = []
_Model_Obj = self.pool[model]
_Node_Obj = self.pool[node_obj]
_Arrow_Obj = self.pool[conn_obj]
for model_key,model_value in _Model_Obj._columns.items():
if model_value._type=='one2many':
if model_value._obj==node_obj:
_Node_Field=model_key
_Model_Field=model_value._fields_id
flag=False
for node_key,node_value in _Node_Obj._columns.items():
if node_value._type=='one2many':
if node_value._obj==conn_obj:
if src_node in _Arrow_Obj._columns and flag:
_Source_Field=node_key
if des_node in _Arrow_Obj._columns and not flag:
_Destination_Field=node_key
flag = True
datas = _Model_Obj.read(cr, uid, id, [],context)
for a in _Node_Obj.read(cr,uid,datas[_Node_Field],[]):
if a[_Source_Field] or a[_Destination_Field]:
nodes_name.append((a['id'],a['name']))
nodes.append(a['id'])
def add_root_tags(self, arch_tree, model, view_id):
for child in arch_tree:
if child.tag == 'data' or child.tag == 'xpath':
child = self.add_root_tags(child, model, view_id)
else:
blank_nodes.append({'id': a['id'],'name':a['name']})
child.attrib.update({
'data-edit-model': model or 'undefined',
'data-edit-view-id': str(view_id),
'data-edit-xpath': '/'
})
return arch_tree
if a.has_key('flow_start') and a['flow_start']:
start.append(a['id'])
else:
if not a[_Source_Field]:
no_ancester.append(a['id'])
for t in _Arrow_Obj.read(cr,uid, a[_Destination_Field],[]):
transitions.append((a['id'], t[des_node][0]))
tres[str(t['id'])] = (a['id'],t[des_node][0])
label_string = ""
if label:
for lbl in eval(label):
if t.has_key(tools.ustr(lbl)) and tools.ustr(t[lbl])=='False':
label_string += ' '
else:
label_string = label_string + " " + tools.ustr(t[lbl])
labels[str(t['id'])] = (a['id'],label_string)
g = graph(nodes, transitions, no_ancester)
g.process(start)
g.scale(*scale)
result = g.result_get()
results = {}
for node in nodes_name:
results[str(node[0])] = result[node[0]]
results[str(node[0])]['name'] = node[1]
return {'nodes': results,
'transitions': tres,
'label' : labels,
'blank_nodes': blank_nodes,
'node_parent_field': _Model_Field,}
def apply_view_inheritance(self, cr, uid, source, inherit_id, context=None):
""" Apply all the (directly and indirectly) inheriting views.
# this really needs to be refixtored
def __view_look_dom(self, cr, user, model, node, view_id, in_tree_view, model_fields, context=None):
:param source: a parent architecture to modify (with parent modifications already applied)
:param inherit_id: the database view_id of the parent view
:return: a modified source where all the modifying architecture are applied
"""
sql_inherit = self.pool.get('ir.ui.view').get_inheriting_views_arch(cr, uid, inherit_id)
for (view_arch, view_id) in sql_inherit:
source = self.apply_inheritance_specs(cr, uid, source, view_arch, view_id, context=context)
source = self.apply_view_inheritance(cr, uid, source, view_id, context=context)
return source
def read_combined(self, cr, uid, view_id, fields=None, context=None):
"""
Utility function to get a view combined with its inherited views.
* Gets the top of the view tree if a sub-view is requested
* Applies all inherited archs on the root view
* Returns the view with all requested fields
.. note:: ``arch`` is always added to the fields list even if not
requested (similar to ``id``)
"""
if context is None: context = {}
# if view_id is not a root view, climb back to the top.
v = self.browse(cr, uid, view_id, context=context)
while v.inherit_id:
v = v.inherit_id
root_id = v.id
# arch and model fields are always returned
if fields:
fields = list(set(fields) | set(['arch', 'model']))
# read the view arch
[view] = self.read(cr, uid, [root_id], fields=fields, context=context)
arch_tree = etree.fromstring( view['arch'].encode('utf-8') if isinstance(view['arch'], unicode) else view['arch'])
# and apply inheritance
arch = self.apply_view_inheritance(cr, uid, arch_tree, root_id, context=context)
return dict(view, arch=etree.tostring(arch, encoding='utf-8'))
# post processing
def postprocess(self, cr, user, model, node, view_id, in_tree_view, model_fields, context=None):
"""Return the description of the fields in the node.
In a normal call to this method, node is a complete view architecture
@ -692,7 +477,7 @@ class view(osv.osv):
new_xml = etree.fromstring(encode(xml))
ctx = context.copy()
ctx['base_model_name'] = model
xarch, xfields = self.__view_look_dom_arch(cr, user, node.get('object'), new_xml, view_id, ctx)
xarch, xfields = self.postprocess_and_fields(cr, user, node.get('object'), new_xml, view_id, ctx)
views['form'] = {
'arch': xarch,
'fields': xfields
@ -719,7 +504,7 @@ class view(osv.osv):
node.remove(f)
ctx = context.copy()
ctx['base_model_name'] = Model
xarch, xfields = self.__view_look_dom_arch(cr, user, column._obj or None, f, view_id, ctx)
xarch, xfields = self.postprocess_and_fields(cr, user, column._obj or None, f, view_id, ctx)
views[str(f.tag)] = {
'arch': xarch,
'fields': xfields
@ -803,7 +588,7 @@ class view(osv.osv):
for f in node:
if children or (node.tag == 'field' and f.tag in ('filter','separator')):
fields.update(self.__view_look_dom(cr, user, model, f, view_id, in_tree_view, model_fields, context))
fields.update(self.postprocess(cr, user, model, f, view_id, in_tree_view, model_fields, context))
orm.transfer_modifiers_to_node(modifiers, node)
return fields
@ -833,11 +618,11 @@ class view(osv.osv):
button.set('readonly', str(int(not can_click)))
return node
def __view_look_dom_arch(self, cr, user, model, node, view_id, context=None):
def postprocess_and_fields(self, cr, user, model, node, view_id, context=None):
""" Return an architecture and a description of all the fields.
The field description combines the result of fields_get() and
__view_look_dom().
postprocess().
:param node: the architecture as as an etree
:return: a tuple (arch, fields) where arch is the given node as a
@ -860,7 +645,7 @@ class view(osv.osv):
elif Model:
fields = Model.fields_get(cr, user, None, context)
fields_def = self.__view_look_dom(cr, user, model, node, view_id, False, fields, context=context)
fields_def = self.postprocess(cr, user, model, node, view_id, False, fields, context=context)
node = self._disable_workflow_buttons(cr, user, model, node)
if node.tag in ('kanban', 'tree', 'form', 'gantt'):
for action, operation in (('create', 'create'), ('delete', 'unlink'), ('edit', 'write')):
@ -887,7 +672,49 @@ class view(osv.osv):
raise orm.except_orm('View error', msg)
return arch, fields
def _get_arch(self, cr, uid, id_, context=None):
# view used as templates
def clean_anotations(arch, parent_info=None):
for child in arch:
if child.tag == 't' or child.tag == 'field':
# can not anote t and field while cleaning
continue
child_text = "".join([etree.tostring(x) for x in child])
if child.attrib.get('data-edit-model'):
if child_text.find('data-edit-model') != -1 or child_text.find('<t ') != -1:
# enclosed tag, move present tag to childs
parent_info = {
'model': child.attrib.get('data-edit-model'),
'view_id': child.attrib.get('data-edit-view-id'),
'xpath': child.attrib.get('data-edit-xpath')+'/'+child.tag,
}
child.attrib.pop('data-edit-model')
child.attrib.pop('data-edit-view-id')
child.attrib.pop('data-edit-xpath')
child = clean_anotations(child, parent_info)
else:
# tagged node, no enclosed, can keep, don't care if had parent_info
continue
else:
if child_text.find('data-edit-model') != -1 or child_text.find('<t ') != -1:
# has other nodes to clean
if parent_info:
# update xpath
parent_info.update({'xpath': parent_info.get('xpath')+'/'+child.tag})
child = clean_anotations(child, parent_info)
elif parent_info:
# put tag from parent, no enclose, higher
child.attrib.update({
'data-edit-model': parent_info.get('model'),
'data-edit-view-id': parent_info.get('view_id'),
'data-edit-xpath': parent_info.get('xpath'),
})
return arch
def read_template(self, cr, uid, id_, context=None):
from pprint import pprint as pp
pp(id_)
try:
@ -921,7 +748,7 @@ class view(osv.osv):
def render(self, cr, uid, id_or_xml_id, values, context=None):
def loader(name):
arch = self._get_arch(cr, uid, name, context=context)
arch = self.read_template(cr, uid, name, context=context)
# parse arch
# on the root tag of arch add the attribute t-name="<name>"
arch = u'<?xml version="1.0" encoding="utf-8"?><tpl><t t-name="{0}">{1}</t></tpl>'.format(name, arch)
@ -929,6 +756,75 @@ class view(osv.osv):
engine = qweb.QWebXml(loader)
return engine.render(id_or_xml_id, values)
# maybe used to print the workflow ?
def graph_get(self, cr, uid, id, model, node_obj, conn_obj, src_node, des_node, label, scale, context=None):
nodes=[]
nodes_name=[]
transitions=[]
start=[]
tres={}
labels={}
no_ancester=[]
blank_nodes = []
_Model_Obj = self.pool[model]
_Node_Obj = self.pool[node_obj]
_Arrow_Obj = self.pool[conn_obj]
for model_key,model_value in _Model_Obj._columns.items():
if model_value._type=='one2many':
if model_value._obj==node_obj:
_Node_Field=model_key
_Model_Field=model_value._fields_id
flag=False
for node_key,node_value in _Node_Obj._columns.items():
if node_value._type=='one2many':
if node_value._obj==conn_obj:
if src_node in _Arrow_Obj._columns and flag:
_Source_Field=node_key
if des_node in _Arrow_Obj._columns and not flag:
_Destination_Field=node_key
flag = True
datas = _Model_Obj.read(cr, uid, id, [],context)
for a in _Node_Obj.read(cr,uid,datas[_Node_Field],[]):
if a[_Source_Field] or a[_Destination_Field]:
nodes_name.append((a['id'],a['name']))
nodes.append(a['id'])
else:
blank_nodes.append({'id': a['id'],'name':a['name']})
if a.has_key('flow_start') and a['flow_start']:
start.append(a['id'])
else:
if not a[_Source_Field]:
no_ancester.append(a['id'])
for t in _Arrow_Obj.read(cr,uid, a[_Destination_Field],[]):
transitions.append((a['id'], t[des_node][0]))
tres[str(t['id'])] = (a['id'],t[des_node][0])
label_string = ""
if label:
for lbl in eval(label):
if t.has_key(tools.ustr(lbl)) and tools.ustr(t[lbl])=='False':
label_string += ' '
else:
label_string = label_string + " " + tools.ustr(t[lbl])
labels[str(t['id'])] = (a['id'],label_string)
g = graph(nodes, transitions, no_ancester)
g.process(start)
g.scale(*scale)
result = g.result_get()
results = {}
for node in nodes_name:
results[str(node[0])] = result[node[0]]
results[str(node[0])]['name'] = node[1]
return {'nodes': results,
'transitions': tres,
'label' : labels,
'blank_nodes': blank_nodes,
'node_parent_field': _Model_Field,}
class view_sc(osv.osv):
_name = 'ir.ui.view_sc'
_columns = {

View File

@ -634,27 +634,6 @@ class MetaModel(type):
if not self._custom:
self.module_to_models.setdefault(self._module, []).append(self)
class FallbackViewMapping(collections.defaultdict):
def __init__(self, cr, uid, model, context=None):
super(FallbackViewMapping, self).__init__()
self.cr = cr
self.uid = uid
self.model = model
self.context = context
def __missing__(self, view_type):
try:
arch = getattr(self.model, '_get_default_%s_view' % view_type)(
self.cr, self.uid, self.context)
except AttributeError:
raise except_orm(_('Invalid Architecture!'), _("There is no view of type '%s' defined for the structure!") % view_type)
return {
'id': 0,
'type': view_type,
'name': 'default',
'field_parent': False,
'arch': arch,
}
# Definition of log access columns, automatically added to models if
# self._log_access is True
@ -1796,15 +1775,12 @@ class BaseModel(object):
return view
def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
"""
Get the detailed composition of the requested view like fields, model, view architecture
:param cr: database cursor
:param user: current user id
:param view_id: id of the view or None
:param view_type: type of the view to return if view_id is None ('form', tree', ...)
:param context: context arguments, like lang, time zone
:param toolbar: true to include contextual actions
:param submenu: deprecated
:return: dictionary describing the composition of the requested view (including inherited views and extensions)
@ -1812,69 +1788,72 @@ class BaseModel(object):
* if the inherited view has unknown position to work with other than 'before', 'after', 'inside', 'replace'
* if some tag other than 'position' is found in parent view
:raise Invalid ArchitectureError: if there is view type other than form, tree, calendar, search etc defined on the structure
"""
if context is None:
context = {}
View = self.pool['ir.ui.view']
view_ref = context.get(view_type + '_view_ref')
if view_ref and not view_id and '.' in view_ref:
module, view_ref = view_ref.split('.', 1)
cr.execute("SELECT res_id FROM ir_model_data WHERE model='ir.ui.view' AND module=%s AND name=%s", (module, view_ref))
view_ref_res = cr.fetchone()
if view_ref_res:
view_id = view_ref_res[0]
root_view = View.read_combined(
cr, user, view_id, view_type, self._name, fields=[
'id', 'name', 'field_parent', 'type', 'model', 'arch'
], fallback=FallbackViewMapping(cr, user, self, context=context),
context=context)
result = {
'model': self._name,
'arch': root_view['arch'],
'type': root_view['type'],
'view_id': root_view['id'],
'name': root_view['name'],
'field_parent': root_view['field_parent'] or False
'field_parent': False,
}
# try to find a view_id if none provided
if not view_id:
# <view_type>_view_ref in context can be used to overrride the default view
view_ref = context.get(view_type + '_view_ref')
if view_ref and '.' in view_ref:
module, view_ref = view_ref.split('.', 1)
cr.execute("SELECT res_id FROM ir_model_data WHERE model='ir.ui.view' AND module=%s AND name=%s", (module, view_ref))
view_ref_res = cr.fetchone()
if view_ref_res:
view_id = view_ref_res[0]
else:
# otherwise try to find the lowest priority matching ir.ui.view
view_id = View.default_view(cr, uid, self._name, view_type, context=context)
if view_id:
# read the view with inherited views applied
root_view = View.read_combined(cr, uid, view_id, fields=['id', 'name', 'field_parent', 'type', 'model', 'arch'], context=context)
result['arch'] = root_view['arch']
result['name'] = root_view['name']
result['type'] = root_view['type']
result['view_id'] = root_view['id']
result['field_parent'] = root_view['field_parent']
else:
# fallback on default views methods if no ir.ui.view could be found
try:
get_func = getattr(self, '_get_default_%s_view' % view_type)
arch_etree = get_func(self.cr, self.uid, self.context)
result['arch'] = etree.tostring(arch_etree, encoding='utf-8')
result['type'] = view_type
result['name'] = 'default'
except AttributeError:
raise except_orm(_('Invalid Architecture!'), _("No default view of type '%s' could be found !") % view_type)
# Apply post processing, groups and modifiers etc...
ctx = context
if root_view.get('model') != self._name:
ctx = dict(context, base_model_name=root_view.get('model'))
xarch, xfields = View._view__view_look_dom_arch(
cr, user, self._name, etree.fromstring(result['arch']),
result['view_id'], context=ctx)
xarch, xfields = View.postprocess_and_fields( cr, uid, self._name, etree.fromstring(result['arch']), result['view_id'], context=ctx)
result['arch'] = xarch
result['fields'] = xfields
# Add related action information if aksed
if toolbar:
toclean = ('report_sxw_content', 'report_rml_content', 'report_sxw', 'report_rml', 'report_sxw_content_data', 'report_rml_content_data')
def clean(x):
x = x[2]
for key in ('report_sxw_content', 'report_rml_content',
'report_sxw', 'report_rml',
'report_sxw_content_data', 'report_rml_content_data'):
for key in toclean:
if key in x:
del x[key]
return x
ir_values_obj = self.pool.get('ir.values')
resprint = ir_values_obj.get(cr, user, 'action',
'client_print_multi', [(self._name, False)], False,
context)
resaction = ir_values_obj.get(cr, user, 'action',
'client_action_multi', [(self._name, False)], False,
context)
resrelate = ir_values_obj.get(cr, user, 'action',
'client_action_relate', [(self._name, False)], False,
context)
resaction = [clean(action) for action in resaction
if view_type == 'tree' or not action[2].get('multi')]
resprint = [clean(print_) for print_ in resprint
if view_type == 'tree' or not print_[2].get('multi')]
resprint = ir_values_obj.get(cr, uid, 'action', 'client_print_multi', [(self._name, False)], False, context)
resaction = ir_values_obj.get(cr, uid, 'action', 'client_action_multi', [(self._name, False)], False, context)
resrelate = ir_values_obj.get(cr, uid, 'action', 'client_action_relate', [(self._name, False)], False, context)
resaction = [clean(action) for action in resaction if view_type == 'tree' or not action[2].get('multi')]
resprint = [clean(print_) for print_ in resprint if view_type == 'tree' or not print_[2].get('multi')]
#When multi="True" set it will display only in More of the list view
resrelate = [clean(action) for action in resrelate
if (action[2].get('multi') and view_type == 'tree') or (not action[2].get('multi') and view_type == 'form')]
@ -1890,7 +1869,7 @@ class BaseModel(object):
return result
def _view_look_dom_arch(self, cr, uid, node, view_id, context=None):
return self['ir.ui.view']._view__view_look_dom_arch(
return self['ir.ui.view'].postprocess_and_fields(
cr, uid, self._name, node, view_id, context=context)
def search_count(self, cr, user, args, context=None):
@ -3045,31 +3024,6 @@ class BaseModel(object):
self._columns[field_name].required = True
self._columns[field_name].ondelete = "cascade"
#def __getattr__(self, name):
# """
# Proxies attribute accesses to the `inherits` parent so we can call methods defined on the inherited parent
# (though inherits doesn't use Python inheritance).
# Handles translating between local ids and remote ids.
# Known issue: doesn't work correctly when using python's own super(), don't involve inherit-based inheritance
# when you have inherits.
# """
# for model, field in self._inherits.iteritems():
# proxy = self.pool.get(model)
# if hasattr(proxy, name):
# attribute = getattr(proxy, name)
# if not hasattr(attribute, '__call__'):
# return attribute
# break
# else:
# return super(orm, self).__getattr__(name)
# def _proxy(cr, uid, ids, *args, **kwargs):
# objects = self.browse(cr, uid, ids, kwargs.get('context', None))
# lst = [obj[field].id for obj in objects if obj[field]]
# return getattr(proxy, name)(cr, uid, lst, *args, **kwargs)
# return _proxy
def fields_get(self, cr, user, allfields=None, context=None, write_access=True):
""" Return the definition of each field.
@ -4911,6 +4865,32 @@ class BaseModel(object):
""" stuff to do right after the registry is built """
pass
#def __getattr__(self, name):
# """
# Proxies attribute accesses to the `inherits` parent so we can call methods defined on the inherited parent
# (though inherits doesn't use Python inheritance).
# Handles translating between local ids and remote ids.
# Known issue: doesn't work correctly when using python's own super(), don't involve inherit-based inheritance
# when you have inherits.
# """
# for model, field in self._inherits.iteritems():
# proxy = self.pool.get(model)
# if hasattr(proxy, name):
# attribute = getattr(proxy, name)
# if not hasattr(attribute, '__call__'):
# return attribute
# break
# else:
# return super(orm, self).__getattr__(name)
# def _proxy(cr, uid, ids, *args, **kwargs):
# objects = self.browse(cr, uid, ids, kwargs.get('context', None))
# lst = [obj[field].id for obj in objects if obj[field]]
# return getattr(proxy, name)(cr, uid, lst, *args, **kwargs)
# return _proxy
def __getattr__(self, name):
if name.startswith('signal_'):
signal_name = name[len('signal_'):]
@ -4976,12 +4956,12 @@ def itemgetter_tuple(items):
if len(items) == 1:
return lambda gettable: (gettable[items[0]],)
return operator.itemgetter(*items)
class ImportWarning(Warning):
""" Used to send warnings upwards the stack during the import process
"""
pass
def convert_pgerror_23502(model, fields, info, e):
m = re.match(r'^null value in column "(?P<field>\w+)" violates '
r'not-null constraint\n',
@ -4997,6 +4977,7 @@ def convert_pgerror_23502(model, fields, info, e):
'message': message,
'field': field_name,
}
def convert_pgerror_23505(model, fields, info, e):
m = re.match(r'^duplicate key (?P<field>\w+) violates unique constraint',
str(e))