Continued from the previous article
Look at the code first
@api.model
def postprocess(self, model, node, view_id, in_tree_view, model_fields):
"""Return the description of the fields in the node.
In a normal call to this method, node is a complete view architecture
but it is actually possible to give some sub-node (this is used so
that the method can call itself recursively).
Originally, the field descriptions are drawn from the node itself.
But there is now some code calling fields_get() in order to merge some
of those information in the architecture.
"""
result = False
fields = {}
children = True
modifiers = {}
if model not in self.env:
self.raise_view_error(_('Model not found: %(model)s') % dict(model=model), view_id)
Model = self.env[model]
if node.tag in ('field', 'node', 'arrow'):
if node.get('object'):
attrs = {}
views = {}
xml_form = E.form(*(f for f in node if f.tag == 'field'))
xarch, xfields = self.with_context(base_model_name=model).postprocess_and_fields(node.get('object'), xml_form, view_id)
views['form'] = {
'arch': xarch,
'fields': xfields,
}
attrs = {'views': views}
fields = xfields
if node.get('name'):
attrs = {}
field = Model._fields.get(node.get('name'))
if field:
editable = self.env.context.get('view_is_editable', True) and self._field_is_editable(field, node)
children = False
views = {}
for f in node:
if f.tag in ('form', 'tree', 'graph', 'kanban', 'calendar'):
node.remove(f)
xarch, xfields = self.with_context(
base_model_name=model,
view_is_editable=editable,
).postprocess_and_fields(field.comodel_name, f, view_id)
views[str(f.tag)] = {
'arch': xarch,
'fields': xfields,
}
attrs = {'views': views}
if field.comodel_name in self.env and field.type in ('many2one', 'many2many'):
Comodel = self.env[field.comodel_name]
node.set('can_create', 'true' if Comodel.check_access_rights('create', raise_exception=False) else 'false')
node.set('can_write', 'true' if Comodel.check_access_rights('write', raise_exception=False) else 'false')
fields[node.get('name')] = attrs
field = model_fields.get(node.get('name'))
if field:
orm.transfer_field_to_modifiers(field, modifiers)
elif node.tag in ('form', 'tree'):
result = Model.view_header_get(False, node.tag)
if result:
node.set('string', result)
in_tree_view = node.tag == 'tree'
elif node.tag == 'calendar':
for additional_field in ('date_start', 'date_delay', 'date_stop', 'color', 'all_day'):
if node.get(additional_field):
fields[node.get(additional_field).split('.', 1)[0]] = {}
for f in node:
if f.tag == 'filter':
fields[f.get('name')] = {}
if not self._apply_group(model, node, modifiers, fields):
# node must be removed, no need to proceed further with its children
return fields
# The view architeture overrides the python model.
# Get the attrs before they are (possibly) deleted by check_group below
orm.transfer_node_to_modifiers(node, modifiers, self._context, in_tree_view)
for f in node:
if children or (node.tag == 'field' and f.tag in ('filter', 'separator')):
fields.update(self.postprocess(model, f, view_id, in_tree_view, model_fields))
orm.transfer_modifiers_to_node(modifiers, node)
return fields
The comment is very clear, according to the xml return Return the description of the fields in the node.
The key function is processed by recursive postprocess_and_fields. The flow inside seems like the previous one. I won't analyze it
Look at the reloadable
How are some attributes in xml, attr state and other attributes handled?
The key function transfer_node_to_modifiers
def transfer_node_to_modifiers(node, modifiers, context=None, in_tree_view=False):
if node.get('attrs'):
modifiers.update(safe_eval(node.get('attrs')))
if node.get('states'):
if 'invisible' in modifiers and isinstance(modifiers['invisible'], list):
# TODO combine with AND or OR, use implicit AND for now.
modifiers['invisible'].append(('state', 'not in', node.get('states').split(',')))
else:
modifiers['invisible'] = [('state', 'not in', node.get('states').split(','))]
for a in ('invisible', 'readonly', 'required'):
if node.get(a):
v = bool(safe_eval(node.get(a), {'context': context or {}}))
if in_tree_view and a == 'invisible':
# Invisible in a tree view has a specific meaning, make it a
# new key in the modifiers attribute.
modifiers['column_invisible'] = v
elif v or (a not in modifiers or not isinstance(modifiers[a], list)):
# Don't set the attribute to False if a dynamic value was
# provided (i.e. a domain from attrs or states).
modifiers[a] = v
You can see that the attributes are processed here.
Therefore, a lot of code on the Internet is also rewritten through the transfer_node_to_modifiers function to put the uid into attrs. . (I'll write this when I have time, (eval))
Return after processing
def transfer_modifiers_to_node(modifiers, node):
if modifiers:
simplify_modifiers(modifiers)
node.set('modifiers', json.dumps(modifiers))
Go in for json
These two functions will be useful. do not forget.
Send a loadview arch
<field name="pr_unit" string="单价" attrs="{"invisible": [("active", "=", False)]}" on_change="1" modifiers="{"invisible": [["active", "=", false]]}"/>\n
<field name="product_uom_qty" string="数量" attrs="{"invisible": [("active", "=", False)]}" on_change="1" modifiers="{"invisible": [["active", "=", false]], "required": true}"/>\n
<field name="price_subtotal" string="总额" attrs="{"invisible": [("active", "=", False)]}" modifiers="{"readonly": true, "invisible": [["active", "=", false]]}"]]}"/>
return
You can see that the attrs attributes are all in the modifier. Return to the front-end template for processing