Odoo 16 Development Book

Model Methods and Decorators

The class in Odoo models contains field declarations as well as business logic methods. The method defined for a button click is one example of a simple type of method.

For example, clicking the confirm button in a form view changes the state from 'Draft' to 'Confirmed.'

The function will first be declared from the XML:

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

The form view will include a 'Confirm' button, whose python action is specified in the button's name attribute (action confirm).

def action_confirm(self):
   self.state = 'confirmed'

In this case, action confirm is a Python method with self as an argument.'self,' the first argument method of Odoo models, can take additional arguments.


Decorators allow you to change the behavior of a method. It simply adds functionality to a function before returning it.


The daily vacuum cron job calls the methods decorated by this decorator (model ir.autovacuum). It is used for tasks that do not require a specific cron job.


Fields from the particular model are the arguments given to the decorator. When any of the fields are altered, the function will be called.

@constrains only support field names, not dotted fields(related fields, Eg:partner_id.city).

@constrains will only be executed if the defined fields in the decorated method are used in the model's create or write function.

It implies that fields won't cause the function to be called during record generation if they don't exist in the view.

An override of create is required to ensure that a constraint is always active.


This decorator can be used to declare the field dependencies of the "compute" method.Each argument must be a string made up 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 can be used to declare the context dependencies of a non-stored "compute" method. Each passed argument is 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)


Decorate a record-style method in which self is a recordset but the contents of its are irrelevant; only its model is.

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 view's fields will be passed as arguments to the 'onchange' decorator. The method will be called when the view's field is modified. The method is called on a pseudo-record that contains the form's values. The field assignments on that record are automatically returned 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'},}

@onchange only supports simple field names, relational fields are not supported(partner_id.city)

Because @onchange returns a recordset of pseudo-records, trying to call any of the CRUD methods (create(), read(), write(), unlink()) on this recordset results in undefined behavior, because the records may not yet exist in the database.

Instead, we could use the update() method or easily set the field as in this example.

Onchange cannot be used to modify a one2many or many2many field.

@api.ondelete(*, at_uninstall)

Mark a method to be executed during unlink().

The methods decorated with @ondelete should raise an error if certain conditions are met, as well as the method should be named either _unlink_if_<condition> or _unlink_except_ <not_condition>.

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!")



at_uninstall (bool) – When the module that implements this function is uninstalled, this decorated method will be called. It should nearly always be False to prevent those errors from occurring upon module uninstalling.

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

For methods that return model instances return a decorator.


  • model – For the current model, a model name or'self'
  • downgrade – The fuction, downgrade(self, value, *args, **kwargs) that converts a record-style value to the traditional-style output.
  • upgrade – A function upgrade(self, value, *args, **kwargs) is 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 modifies the method's output to be in 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 should be noted that the decorated method must satisfy to that convention.

Those decorators are automatically inherited: A method that overrides an existing decorated method will be decorated in the same way.@returns(model).



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