[ADD] behavior and tests for default & ancestors, fallback handling in read_combined
bzr revid: xmo@openerp.com-20130424130907-6d6lkrkrgqv0hsfq
This commit is contained in:
parent
8715746672
commit
fb492d98ba
|
@ -29,6 +29,7 @@ from openerp import tools
|
||||||
from openerp.osv import fields,osv
|
from openerp.osv import fields,osv
|
||||||
from openerp.tools import graph, SKIPPED_ELEMENT_TYPES
|
from openerp.tools import graph, SKIPPED_ELEMENT_TYPES
|
||||||
from openerp.tools.safe_eval import safe_eval as eval
|
from openerp.tools.safe_eval import safe_eval as eval
|
||||||
|
from openerp.tools.translate import _
|
||||||
from openerp.tools.view_validation import valid_view
|
from openerp.tools.view_validation import valid_view
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
@ -51,6 +52,9 @@ class view_custom(osv.osv):
|
||||||
class view(osv.osv):
|
class view(osv.osv):
|
||||||
_name = 'ir.ui.view'
|
_name = 'ir.ui.view'
|
||||||
|
|
||||||
|
class NoViewError(Exception): pass
|
||||||
|
class NoDefaultError(NoViewError): pass
|
||||||
|
|
||||||
def _type_field(self, cr, uid, ids, name, args, context=None):
|
def _type_field(self, cr, uid, ids, name, args, context=None):
|
||||||
result = {}
|
result = {}
|
||||||
for record in self.browse(cr, uid, ids, context):
|
for record in self.browse(cr, uid, ids, context):
|
||||||
|
@ -166,7 +170,7 @@ class view(osv.osv):
|
||||||
cr.execute('CREATE INDEX ir_ui_view_model_type_inherit_id ON ir_ui_view (model, inherit_id)')
|
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,
|
def read_combined(self, cr, uid, view_id, view_type, model,
|
||||||
fields=None, context=None):
|
fields=None, fallback=None, context=None):
|
||||||
"""
|
"""
|
||||||
Utility function stringing together all method calls necessary to get
|
Utility function stringing together all method calls necessary to get
|
||||||
a full, final view:
|
a full, final view:
|
||||||
|
@ -174,8 +178,7 @@ class view(osv.osv):
|
||||||
* Gets the default view if no view_id is provided
|
* Gets the default view if no view_id is provided
|
||||||
* Gets the top of the view tree if a sub-view is requested
|
* Gets the top of the view tree if a sub-view is requested
|
||||||
* Applies all inherited archs on the root view
|
* Applies all inherited archs on the root view
|
||||||
* Applies post-processing both generic (ir.ui.view) and specific
|
* Applies post-processing
|
||||||
(ir.ui.view.$type)
|
|
||||||
* Returns the view with all requested fields
|
* Returns the view with all requested fields
|
||||||
|
|
||||||
.. note:: ``arch`` is always added to the fields list even if not
|
.. note:: ``arch`` is always added to the fields list even if not
|
||||||
|
@ -183,29 +186,47 @@ class view(osv.osv):
|
||||||
|
|
||||||
If no view is available (no view_id or invalid view_id provided, or
|
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
|
no view stored for (model, view_type)) a view record will be fetched
|
||||||
from the ``defaults`` mapping.
|
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
|
||||||
"""
|
"""
|
||||||
if not view_id:
|
if context is None: context = {}
|
||||||
view_id = self.default_view(cr, uid, model, view_type, context=context)
|
try:
|
||||||
root_id = self.root_ancestor(cr, uid, view_id, context=context)
|
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 and 'arch' not in fields:
|
if fields and 'arch' not in fields:
|
||||||
fields = list(itertools.chain(['arch'], fields))
|
fields = list(itertools.chain(['arch'], fields))
|
||||||
|
|
||||||
[view] = self.read(cr, uid, [root_id], fields=fields, context=context)
|
[view] = self.read(cr, uid, [root_id], fields=fields, context=context)
|
||||||
|
|
||||||
arch_tree = etree.fromstring(
|
arch_tree = etree.fromstring(
|
||||||
view['arch'].encode('utf-8') if isinstance(view['arch'], unicode)
|
view['arch'].encode('utf-8') if isinstance(view['arch'], unicode)
|
||||||
else view['arch'])
|
else view['arch'])
|
||||||
descendants = self.iter(
|
descendants = self.iter(
|
||||||
cr, uid, view['id'], model, exclude_base=True, context=context)
|
cr, uid, view['id'], model, exclude_base=True, context=context)
|
||||||
arch = self.apply_inherited_archs(
|
arch = self.apply_inherited_archs(
|
||||||
cr, uid, arch_tree, descendants,
|
cr, uid, arch_tree, descendants,
|
||||||
model, view['id'], context=context)
|
model, view['id'], context=context)
|
||||||
|
|
||||||
|
if view['model'] != model:
|
||||||
|
context = dict(context, base_model_name=view['model'])
|
||||||
|
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)
|
||||||
|
|
||||||
# TODO: post-processing
|
# TODO: post-processing
|
||||||
|
|
||||||
return dict(view, arch=arch)#etree.tostring(arch, 'utf-8'))
|
return dict(view, arch=etree.tostring(arch, encoding='utf-8'))
|
||||||
|
|
||||||
def locate_node(self, arch, spec):
|
def locate_node(self, arch, spec):
|
||||||
""" Locate a node in a source (parent) architecture.
|
""" Locate a node in a source (parent) architecture.
|
||||||
|
@ -308,11 +329,16 @@ class view(osv.osv):
|
||||||
:return: id of the default view for the (model, view_type) pair
|
:return: id of the default view for the (model, view_type) pair
|
||||||
:rtype: int
|
:rtype: int
|
||||||
"""
|
"""
|
||||||
return self.search(cr, uid, [
|
ids = self.search(cr, uid, [
|
||||||
['model', '=', model],
|
['model', '=', model],
|
||||||
['type', '=', view_type],
|
['type', '=', view_type],
|
||||||
['inherit_id', '=', False],
|
['inherit_id', '=', False],
|
||||||
], limit=1, order='priority', context=context)[0]
|
], 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):
|
def root_ancestor(self, cr, uid, view_id, context=None):
|
||||||
"""
|
"""
|
||||||
|
@ -325,6 +351,9 @@ class view(osv.osv):
|
||||||
:return: id of the root view for the tree
|
:return: id of the root view for the tree
|
||||||
"""
|
"""
|
||||||
view = self.browse(cr, uid, view_id, context=context)
|
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.
|
# Search for a root (i.e. without any parent) view.
|
||||||
while view.inherit_id:
|
while view.inherit_id:
|
||||||
|
|
|
@ -631,6 +631,27 @@ class MetaModel(type):
|
||||||
if not self._custom:
|
if not self._custom:
|
||||||
self.module_to_models.setdefault(self._module, []).append(self)
|
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
|
# Definition of log access columns, automatically added to models if
|
||||||
# self._log_access is True
|
# self._log_access is True
|
||||||
|
@ -2054,8 +2075,6 @@ class BaseModel(object):
|
||||||
context = {}
|
context = {}
|
||||||
View = self.pool['ir.ui.view']
|
View = self.pool['ir.ui.view']
|
||||||
|
|
||||||
result = {'type': view_type, 'model': self._name}
|
|
||||||
|
|
||||||
view_ref = context.get(view_type + '_view_ref')
|
view_ref = context.get(view_type + '_view_ref')
|
||||||
|
|
||||||
if view_ref and not view_id and '.' in view_ref:
|
if view_ref and not view_id and '.' in view_ref:
|
||||||
|
@ -2068,37 +2087,23 @@ class BaseModel(object):
|
||||||
root_view = View.read_combined(
|
root_view = View.read_combined(
|
||||||
cr, user, view_id, view_type, self._name, fields=[
|
cr, user, view_id, view_type, self._name, fields=[
|
||||||
'id', 'name', 'field_parent', 'type', 'model', 'arch'
|
'id', 'name', 'field_parent', 'type', 'model', 'arch'
|
||||||
], context=context)
|
], fallback=FallbackViewMapping(cr, user, self, context=context),
|
||||||
if root_view:
|
context=context)
|
||||||
result.update(
|
|
||||||
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)
|
|
||||||
else:
|
|
||||||
# otherwise, build some kind of default view
|
|
||||||
try:
|
|
||||||
view = getattr(self, '_get_default_%s_view' % view_type)(
|
|
||||||
cr, user, context)
|
|
||||||
except AttributeError:
|
|
||||||
# what happens here, graph case?
|
|
||||||
raise except_orm(_('Invalid Architecture!'), _("There is no view of type '%s' defined for the structure!") % view_type)
|
|
||||||
|
|
||||||
result.update(
|
result = {
|
||||||
arch=view,
|
'model': self._name,
|
||||||
name='default',
|
'arch': root_view['arch'],
|
||||||
field_parent=False,
|
'type': root_view['type'],
|
||||||
view_id=0)
|
'view_id': root_view['id'],
|
||||||
|
'name': root_view['name'],
|
||||||
|
'field_parent': root_view['field_parent'] or False
|
||||||
|
}
|
||||||
|
|
||||||
parent_view_model = root_view['model'] if root_view else None
|
ctx = context
|
||||||
if parent_view_model != self._name:
|
if root_view.get('model') != self._name:
|
||||||
ctx = context.copy()
|
ctx = dict(context, base_model_name=root_view.get('model'))
|
||||||
ctx['base_model_name'] = parent_view_model
|
|
||||||
else:
|
|
||||||
ctx = context
|
|
||||||
xarch, xfields = self.__view_look_dom_arch(
|
xarch, xfields = self.__view_look_dom_arch(
|
||||||
cr, user, result['arch'], result['view_id'], context=ctx)
|
cr, user, etree.fromstring(result['arch']), result['view_id'], context=ctx)
|
||||||
result['arch'] = xarch
|
result['arch'] = xarch
|
||||||
result['fields'] = xfields
|
result['fields'] = xfields
|
||||||
|
|
||||||
|
|
|
@ -184,12 +184,9 @@ class TestViewInheritance(common.TransactionCase):
|
||||||
self.cr, self.uid, view_id=self.ids['B1'])
|
self.cr, self.uid, view_id=self.ids['B1'])
|
||||||
self.assertEqual(root_id, self.ids['B'])
|
self.assertEqual(root_id, self.ids['B'])
|
||||||
|
|
||||||
@unittest2.skip("What should the behavior be when no ancestor is found "
|
|
||||||
"because view_id is invalid?")
|
|
||||||
def test_no_root_ancestor(self):
|
def test_no_root_ancestor(self):
|
||||||
root = self.View.root_ancestor(
|
with self.assertRaises(self.View.NoViewError):
|
||||||
self.cr, self.uid, view_id=12345678)
|
self.View.root_ancestor(self.cr, self.uid, view_id=12345678)
|
||||||
self.assertFalse(root)
|
|
||||||
|
|
||||||
def test_default_view(self):
|
def test_default_view(self):
|
||||||
default = self.View.default_view(
|
default = self.View.default_view(
|
||||||
|
@ -200,16 +197,22 @@ class TestViewInheritance(common.TransactionCase):
|
||||||
self.cr, self.uid, model=self.model, view_type='tree')
|
self.cr, self.uid, model=self.model, view_type='tree')
|
||||||
self.assertEqual(default_tree, self.ids['C'])
|
self.assertEqual(default_tree, self.ids['C'])
|
||||||
|
|
||||||
@unittest2.skip("What should the behavior be when no default is found "
|
|
||||||
"because model does not exist or no view for model?")
|
|
||||||
def test_no_default_view(self):
|
def test_no_default_view(self):
|
||||||
default = self.View.default_view(
|
with self.assertRaises(self.View.NoDefaultError):
|
||||||
self.cr, self.uid, model='does.not.exist', view_type='form')
|
self.View.default_view(
|
||||||
self.assertFalse(default)
|
self.cr, self.uid, model='does.not.exist', view_type='form')
|
||||||
|
|
||||||
default = self.View.default_view(
|
with self.assertRaises(self.View.NoDefaultError):
|
||||||
self.cr, self.uid, model=self.model, view_type='graph')
|
self.View.default_view(
|
||||||
self.assertFalse(default)
|
self.cr, self.uid, model=self.model, view_type='graph')
|
||||||
|
|
||||||
|
@unittest2.skip("Not tested")
|
||||||
|
def test_apply_inherited_archs(self):
|
||||||
|
self.fail()
|
||||||
|
|
||||||
|
@unittest2.skip("Not tested")
|
||||||
|
def test_apply_inheritance_specs(self):
|
||||||
|
self.fail()
|
||||||
|
|
||||||
class TestViewCombined(common.TransactionCase):
|
class TestViewCombined(common.TransactionCase):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue