Dynamic Window Actions in Odoo 14: Server Actions and Programmatic Navigation
Odoo’s ir.actions.act_window enables dynamic navigation to views of models, and when combined with server actions or JavaScript triggers, it allows context-aware filtering and targeted UI behavior.
Defining a Window Action with Custom View References A window action can be declared in XML to specify which views should be used when opening a model, overriding defaults with custom references:
<record id="res_company_user_action" model="ir.actions.act_window">
<field name="name">Users</field>
<field name="res_model">res.users</field>
<field name="view_mode">tree,form,kanban</field>
<field name="context">
{
'form_view_ref': 'ship_manage.res_company_user_view_form',
'tree_view_ref': 'ship_manage.res_company_user_view_tree',
'kanban_view_ref': 'ship_manage.res_company_user_view_kanban',
'search_view_ref': 'ship_manage.res_company_user_view_search'
}
</field>
</record>
This configuration ensures that when the action is invoked, Odoo loads the explicitly referenced views instead of relying on the model’s default view definitions.
Filtering Data via Server Actions Server actions can modify the domain of a window action dynamically based on the current user’s context. For instance, to restrict user records to those belonging to the user’s company:
<record model="ir.actions.server" id="res_company_user_server_action">
<field name="name">Filter Users by Company</field>
<field name="model_id" ref="base.model_res_users"/>
<field name="state">code</field>
<field name="code">
action = env.ref('ship_manage.res_company_user_action').read()[0]
action['domain'] = [('company_id', '=', env.user.company_id.id)]
return action
</field>
</record>
The server action retrieves the existing window action, modifies its domain to include a company filter, and returns the updated action object. This approach is ideal for scenarios where record-level security via ir.rule is insuffficient or too rigid.
Programmatically Creating Window Actions in Server Code Instead of referencing a predefined action, you can construct one entirely in code:
<record model="ir.actions.server" id="res_company_action1">
<field name="name">Direct Company Form</field>
<field name="model_id" ref="base.model_res_company"/>
<field name="state">code</field>
<field name="code">
action = {
'name': 'Company Test',
'type': 'ir.actions.act_window',
'res_model': 'res.company',
'view_mode': 'form',
'view_type': 'form',
'target': 'main',
'res_id': env.user.company_id.id,
'context': {'form_view_ref': 'ship_manage.res_company_view_form_test'}
}
return action
</field>
</record>
This method gives full control over the action’s properties — including target, context, and record ID — without requiring a prior XML declaratoin.
Triggering Actions from Python Buttons In a form view, a button can invoke a server method that returns a window action:
<button name="get_company_action" string="Open Company" type="object"/>
def get_company_action(self):
action = self.env.ref('ship_manage.ship_manage_res_company_action').read()[0]
action['res_id'] = self.env.user.company_id.id
return action
Alternative, define the action inline without referencing an XML record:
def get_company_action(self):
rb = self.env['bill.head'].browse(self.env.context.get('active_id'))
return {
'name': _('Repair Bill'),
'type': 'ir.actions.act_window',
'res_model': 'bill.head',
'view_mode': 'form',
'target': 'main',
'res_id': rb.id,
'context': {'form_view_ref': 'ship.bill_head_view_form'}
}
This approach is useful for actions tied to specific records or dynamic business logic.
Invoking Actions via JavaScript In web client widgets, actions can be triggered using the do_action method. The views parameter defines the sequence and type of views to load:
_onBtnClicked: function (ev) {
this.do_action({
type: 'ir.actions.act_window',
name: this.title,
res_model: this.modelName,
views: [[false, 'list'], [false, 'form']],
domain: ev.data.domain,
});
}
Here, [false, 'list'] means "use the default tree view," and [false, 'form'] means "use the default form view." To use a specific view ID, replace false with the actual view ID (e.g., [123, 'tree']), which can be obtained via ref in XML or fetched dynamically.
For advanced use cases, such as opening a form view with a specific record and custom context, the same structure applies — just set res_id and extend the context object accordingly.
The core implementation of window actions resides in odoo/odoo/addons/base/models/ir_actions.py, where the ir.actions.act_window model handles view resolution, domain evaluation, and context merging.