Odoo 17 Development Book - Server side development

Model Methods and Decorators

In Odoo models, the class encompasses both field declarations and business logic methods. An instance of a straightforward method is the one designated for handling a button click event.

As an illustration, when the "Confirm" button in a form view is clicked, it triggers a transition in the state from 'Draft' to 'Confirmed.' The corresponding function is initially declared in the XML as follows:

<button name="action_confirm" string="Confirm" type="object"/>

The form view is equipped with a 'Confirm' button, and its corresponding Python action is explicitly defined in the button's name attribute as "action_confirm."

state = fields.Selection([
    ('draft', 'Draft'),
    ('confirmed', 'Confirmed')
], default='draft')
def action_confirm(self

In this scenario, "action_confirm" is a Python method with "self" as an argument. The "self," serving as the initial parameter in Odoo models' methods, can accommodate additional arguments.


Decorators enable the modification of a method's behavior by seamlessly incorporating additional functionality to the function before ultimately returning it.


The daily vacuum cron job invokes methods adorned with this decorator, which is applied to the model ir.autovacuum. This mechanism is useful for tasks that don't require a dedicated cron job.


The decorator is provided with fields from the specific model as arguments. Upon any modification to these fields, the associated function is triggered.

It's important to note that @constrains exclusively accepts field names and doesn't support dotted fields such as related fields (e.g., partner_id.city).

The execution of @constrains is contingent on the defined fields within the decorated method being utilized in the model's create or write functions.

This implies that the function won't be invoked during record creation unless the specified fields are present in the view.

To guarantee the consistent activation of a constraint, an override of the create function becomes necessary


This decorator serves to specify the field dependencies for the "compute" method. Each argument should be a string consisting of a dot-separated list of field names:

pname = fields.Char(compute='_compute_pname')
@api.depends('partner_id.name', 'partner_id.is_company')
def _compute_pname(self):
    for record in self:
        if record.partner_id.is_company:
            record.pname = (record.partner_id.name or "").upper()
            record.pname = record.partner_id.name

This decorator is utilized to specify the context dependencies for a non-stored "compute" method. Each provided argument corresponds to a key in the context dictionary:

price = fields.Float(compute='_compute_product_price')
def _compute_product_price(self):
    for product in self:
        if product.env.context.get('pricelist'):
            pricelist = self.env['product.pricelist'].browse(product.env.context['pricelist'])
            pricelist = self.env['product.pricelist'].get_default_pricelist()
        product.price = pricelist.get_products_price(product).get(product.id, 0.0)

Apply a decorator to a record-style method where "self" represents a record set, and the contents of the record set are inconsequential—only the model associated with it matters.

def method(self, args):
    //Add your method here

Decorate a method with a list of dictionaries if it creates several records. It is referred to as a list of dictionaries.

record = model.create(vals)
records = model.create([vals, ...])

The 'onchange' decorator takes the view's fields as arguments. When any of these fields is modified, the associated method is triggered. The method is invoked on a pseudo-record, encompassing the form's values. Any field assignments made on this record are automatically sent back to the client.

def _onchange_partner(self):
    self.message = "Dear %s" % (self.partner_id.name or "")
    return {
       'warning': {'title': "Warning", 'message': "What is this?",'type': 'notification'},}

The 'onchange' decorator exclusively accepts straightforward field names and does not support relational fields like 'partner_id.city.'

Due to the fact that '@onchange' returns a recordset of pseudo-records, attempting to invoke any CRUD methods (create(), read(), write(), unlink()) on this recordset may lead to undefined behavior. This is because the records may not have been created in the database yet.

As an alternative, the update() method or direct field assignment, as demonstrated in this example, can be employed.

It's important to note that 'onchange' cannot be utilized to modify a field of type one2many or many2many.


Use the @ondelete decorator to designate a method for execution during the unlink() operation.Methods adorned with @ondelete should raise an error under specific conditions. Additionally, the method should be named either unlink_if or unlink_except.

def _unlink_if_user_inactive(self):
    if any(user.active for user in self):
        raise UserError("Can't delete an active user!")
# same as above but with _unlink_except_* as method name
def _unlink_except_active_user(self):
    if any(user.active for user in self):
        raise UserError("Can't delete an active user!")

Specify the parameter:

• at_uninstall (boolean): If True, the decorated method will be invoked during the uninstallation of the module that implements it. It is typically set to False to avoid potential errors during module uninstallation.

@api.returns(model, downgrade=None, upgrade=None)


  • model: Represents the current model and can be a model name or 'self.'
  • downgrade: A function, downgrade(self, value, *args, **kwargs), that transforms a record-style value to the traditional-style output.
  • upgrade: A function, upgrade(self, value, *args, **kwargs), used to convert a traditional-style value to a record-style output.

The parameters self, *args, and **kwargs are passed to the method in the record style.The decorator adjusts the method's output to conform to the API style: id, ids, or False for the traditional form style, and the recordset for the record style.

def find_partner(self, arg):
    # return some record
# output depends on call style: traditional vs record style
partner_id = model.find_partner(cr, uid, arg, context=context)
# recs = model.browse(cr, uid, ids, context)
partner_record = recs.find_partner(arg)

It is important to acknowledge that the decorated method must adhere to the specified convention.These decorators are inherited automatically: If a method overrides an existing decorated method, it will be adorned in the same manner, using the @returns(model) decorator.



Cybrosys Technologies Pvt. Ltd.
Neospace, Kinfra Techno Park
Kakkancherry, Calicut
Kerala, India - 673635



Cybrosys Technologies Pvt. Ltd.
1st Floor, Thapasya Building,
Infopark, Kakkanad,
Kochi, India - 682030.



Cybrosys Techno Solutions
The Estate, 8th Floor,
Dickenson Road,
Bangalore, India - 560042

Send Us A Message