How a Button Click in Odoo Triggers Python

February 12, 2026 by
How a Button Click in Odoo Triggers Python
Jasson
| No comments yet

If you've ever added a button to an Odoo form view and written a Python method to go with it, you've probably just trusted that it works. You click the button, something happens on the server, and the record updates. But what actually happens between that click and your Python code running? The journey is surprisingly interesting, and understanding it will make you a significantly better Odoo developer.


It Starts in the XML

Everything begins in your view definition. When you add a button to a form view in Odoo, it looks something like this:

<button name="action_confirm" string="Confirm Order" type="object" class="btn-primary"/>

The two most important attributes here are name and type. The name attribute is the exact name of the Python method that will be called on your model. The type="object" attribute is the instruction that tells Odoo this button should trigger a server-side Python method — as opposed to type="action", which would trigger a window action instead.

That single type="object" attribute is what sets the entire chain of events in motion.


The Browser Intercepts the Click

When the page loads, Odoo's JavaScript framework built on Owl in Odoo 17 and beyond — reads your view definition and renders the button as a standard HTML element. But it also attaches an event listener to it.

When you click the button, the browser fires a click event. Odoo's ViewButton component (or its equivalent handler in the form controller) catches that event before the browser does anything else with it. At this point, control is entirely in JavaScript's hands.

The JS code reads the button's attributes — specifically name, type, and the current record's model and ID — and prepares an RPC call. Nothing has touched Python yet. You're still entirely in the browser.


JavaScript Builds the RPC Payload

With the button metadata and the current record context in hand, Odoo's frontend constructs a JSON-RPC request payload that looks roughly like this:

{
  "jsonrpc": "2.0",
  "method": "call",
  "params": {
    "model": "sale.order",
    "method": "action_confirm",
    "args": [[42]],
    "kwargs": {
      "context": {
        "lang": "en_US",
        "tz": "Asia/Singapore",
        "uid": 2
      }
    }
  }
}

Notice the args field contains [[42]] — a list containing a list with the record's ID. This is Odoo's standard calling convention. The outer list is the positional arguments, and the inner list is the set of record IDs the method should operate on. Even if you click a button on a single record, Odoo wraps the ID in a list because the ORM is designed to work with recordsets — collections of one or more records.

This payload is then sent as an HTTP POST request to Odoo's standard RPC endpoint: /web/dataset/call_kw.


Odoo's Server Receives and Routes the Request

On the Python side, Odoo has a web controller that sits at that /web/dataset/call_kw route and does nothing but receive and dispatch these calls. It reads the model and method from the payload, finds the right model class in Odoo's registry, and instantiates a recordset using the provided IDs.

It looks up sale.order in the model registry, browses record ID 42 to create a recordset, and then calls action_confirm() on that recordset. All the ORM magic — field access, write(), create(), environment context — is available to your method from that moment on.

Your Python method finally runs:

def action_confirm(self):
    for order in self:
        order.state = 'sale'
        order.confirmation_date = fields.Datetime.now()
    return True


The Return Value Matters More Than You Think

What your Python method returns determines what happens next in the browser. This is a part many developers overlook.

If your method returns True or False, Odoo simply refreshes the current form view to reflect any changes made to the record. This is the most common case and the simplest.

If your method returns a dictionary structured like a window action, Odoo's frontend interprets it as an instruction to navigate somewhere — open a new form, load a list view, display a wizard, and so on. This is how buttons that open pop-up dialogs or redirect to related records work.

def action_confirm(self):
    self.state = 'sale'
    return {
        'type': 'ir.actions.act_window',
        'res_model': 'sale.order',
        'res_id': self.id,
        'view_mode': 'form',
    }

The JSON response travels back to the browser, the rpc service resolves its promise with that return value, and the form controller decides what to do next based on its shape.

 

How a Button Click in Odoo Triggers Python
Jasson February 12, 2026
Share this post
Archive
Sign in to leave a comment