Demystifying Odoo and OpenERP Action Dictionaries: How to Return Views from Python Methods
Understanding Odoo's Client-Side Action Mechanism
In Odoo (formerly OpenERP), returning a dictionary from a Python method is a powerful design pattern. Instead of just performing database operations, a method can instruct the web client to execute a specific action—such as opening a new form, launching a wizard, or displaying a filtered list of records.
This mechanism relies on Odoo's Action Manager in the web client. When a Python method returns a dictionary, the client intercepts it. If the dictionary contains a 'type' key, the client treats it as an action definition and processes it accordingly.
The Core Fields: What Do They Mean?
To understand what is mandatory and what is optional, let's break down the key-value pairs commonly used in these return dictionaries:
type(Mandatory): Tells the client what kind of action to execute. For opening views, this is almost always'ir.actions.act_window'.res_model(Mandatory): The technical name of the target model you want to open (e.g.,'account.move'or'sale.order').view_mode(Mandatory/Recommended): A comma-separated string of view types that should be available (e.g.,'tree,form').res_id(Optional): The database ID of a specific record. If provided alongside a'form'view mode, the client will open that specific record instead of a blank creation form.target(Optional): Determines where the view opens. Options include'current'(replaces the main content area) or'new'(opens the view in a modal popup window).context(Optional): A dictionary (or string representation of a dictionary) passed to the view to pre-populate fields or alter view behavior.
Why Do Different Syntaxes (like views vs view_id) Both Work?
You might have noticed that some older code uses 'view_id': [res_id], while newer Odoo code uses 'views': [(view_id, 'form')]. Here is why both work:
The Odoo framework is designed to be highly backward-compatible. Internally, the web client parses these parameters and normalizes them.
- The
viewsparameter: This is the modern and preferred approach. It takes a list of tuples:[(view_id, 'view_type')]. This allows you to specify exact, custom view IDs for each view mode. For example:'views': [(tree_view_id, 'tree'), (form_view_id, 'form')]. - The legacy approach: If you only provide
view_idandview_mode, Odoo will automatically construct the internalviewslist for you behind the scenes.
A Modern, Clean Example
Here is how you should structure this return statement in modern Odoo versions (v10 through v17+):
def action_open_invoice(self):
self.ensure_one()
return {
'name': 'Customer Invoice',
'type': 'ir.actions.act_window',
'res_model': 'account.move',
'view_mode': 'form',
'res_id': self.invoice_id.id,
'target': 'current',
'context': {
'default_move_type': 'out_invoice',
}
}How to Determine What is Mandatory
If you want to know exactly which fields are supported, you can inspect the database structure of the action model itself. In Odoo, navigate to Settings > Technical > Actions > Window Actions. Every field defined on the ir.actions.act_window model can be passed inside your Python return dictionary. If a field is marked as required on that model (like name and res_model), it should be included in your dictionary.