In Odoo, the method decorators are used to do or add additional functionalities with the functions with which we have added the decorator. The method decorators are python functions defined in Odoo/api.py, which can be used with the other functions as wrapper functions.
The method decorators in Odoo 15 are described below:
api.onchange
The onchange method decorator is mostly used with the onchange method for the given fields. The onchange method will be triggered while we change the value of any of the fields specified with the onchange decorator from the form view. The onchange method will be invoked on a pseudo-record that contains all the values of fields present in the form view. Even though we cannot perform the CRUD operations in that record, the field assignments are updated in the form view. The onchange decorator does not support the dotted names(fields of relational fields like product_id.default_code), and those will be ignored. We can only use simple field names.
Here is an example code for @api.onchange:
@api.onchange('analytic_account_id', 'analytic_tag_ids')
def _onchange_mark_recompute_taxes_analytic(self):
''' Trigger tax recomputation only when some taxes with analytics
'''
for line in self:
if not line.tax_repartition_line_id and any(tax.analytic for tax in line.tax_ids):
line.recompute_tax_line = True
api.autovacuum
All the methods defined with the autovacuum method decorator will be invoked by the daily vacuum cron job defined with the model ir.autovacuum. The methods that perform garbage-collection-like tasks and need to be run daily can be defined with the autovacuum decorator. So that we don’t have to create a dedicated cron job for it.
Here is an example code for @api.autovacuum:
@api.autovacuum
def _gc_sessions(self, *args, **kwargs):
"""Remove wishlists for unexisting sessions."""
self.with_context(active_test=False).search([
("create_date", "<", fields.Datetime.to_string(datetime.now() - timedelta(weeks=kwargs.get('wishlist_week', 5)))),
("partner_id", "=", False),
]).unlink()
api.model
The model decorator is used with the methods where self is a record set, but only the model is relevant and not its contents. It helps in the code migration since it will convert the old API calls to the new ones, which makes the migration process faster.
Here is an example code for @api.model:
@api.model
def _get_default_currency(self):
''' Get the default currency from either the journal, either the default journal's company. '''
journal = self._get_default_journal()
return journal.currency_id or journal.company_id.currency_id
api.model_create_multi
The model_create_multi decorator is used with the methods that create multiple records from a single dictionary or a list of dictionaries. The values are passed as parameters to the method.
Here is an example code for @api.model_create_multi:
@api.model_create_multi
def create(self, vals):
cashboxes = super(AccountBankStmtCashWizard, self).create(vals)
cashboxes._validate_cashbox()
return cashboxes
api.returns
The returns decorator is used with the methods that return instances of the model.
Parameters:
model – a string which can be either ‘self’ for the current model or a model name like ‘res.partner’.
downgrade – a function that converts the record-style value to a traditional-style output. downgrade(self, value, *args, **kwargs)
upgrade – a function that converts the traditional-style value to a record-style output. upgrade(self, value, *args, **kwargs)
The arguments self, *args, and **kwargs of the downgrade or upgrade function parameters are the same, which are passed to the method in the record-style.
A method that overrides an existing method with the @api.returns method decorator will be decorated with the same @api.returns method decorator. Since these decorators are inherited automatically.
Here is an example code for @api.returns:
@api.returns('self', lambda value: value.id)
def copy(self, default=None):
default = dict(default or {})
if 'name' not in default:
default['name'] = _("%s (Copy)") % self.name
return super(AccountTax, self).copy(default=default)
api.ondelete
The ondelete decorator is used to mark a method which is to be called on unlinking the records. This is used to prevent the deletion of records, like lines on posted entries while uninstalling the module which cannot be obtained by overriding the unlink() method.
The methods with ondelete decorators check for some conditions specified in them and raise an error based on the result. The method should be named like either _unlink_if_<condition> or _unlink_except_<not_condition>.
Parameters:
at_uninstall (bool) – Whether the decorated method should be called if the module is being uninstalled. If False, the module uninstallation does not trigger those errors.
Here is an example code for @api.ondelete:
@api.ondelete(at_uninstall=False)
def _unlink_if_not_done(self):
if any(batch.state == 'done' for batch in self):
raise UserError(_("You cannot delete Done batch transfers."))
# same as the above but with _unlink_except_* as the method name
@api.ondelete(at_uninstall=False)
def _unlink_except_done(self):
if any(batch.state == 'done' for batch in self):
raise UserError(_("You cannot delete Done batch transfers."))
api.depends
The depends decorator is used to specify the compute dependencies for a computing method. Each argument must be a string of field names, where it can be a simple or a dot-separated sequence of field names. It is possible to pass a single function as an argument, where the dependencies are given by calling the function with the field’s model.
Here is an example code for @api.depends:
complete_name = fields.Char('Complete Name', compute='_compute_complete_name', store=True)
@api.depends('name', 'parent_id.complete_name')
def _compute_complete_name(self):
for department in self:
if department.parent_id:
department.complete_name = '%s / %s' % (
department.parent_id.complete_name, department.name)
else:
department.complete_name = department.name
api.depends_context
The depends_context decorator is used with non-stored “compute” methods to specify the context dependencies for the methods. The arguments passed will be the key in the context’s dictionary and the dependencies must be hashable.
In this decorator, these keys have special support:
company: value in context or current company id
uid: current user id and superuser flag
active_test: value in env.context or value in field.context
Here is an example code for @api.depends_context:
tz_mismatch = fields.Boolean(compute='_compute_tz_mismatch')
@api.depends('tz')
@api.depends_context('uid')
def _compute_tz_mismatch(self):
for leave in self:
leave.tz_mismatch = leave.tz != self.env.user.tz
api.constrains
The constrains decorator specifies constraint dependencies for a method. The arguments should be the field names on which the checks will be performed. The decorator will be triggered only if the fields specified as the arguments are included in the creating or write call, and if they are present in the view. The constrains decorator also supports only the simple field names like the onchange decorator, and it ignores the dotted names. In most of the cases, we will be raising a ValidationError if the check fails.
Here is an example code for @api.constrains:
@api.constrains('reconcile', 'internal_group', 'tax_ids')
def _constrains_reconcile(self):
for record in self:
if record.internal_group == 'off_balance':
if record.reconcile:
raise UserError(_('An Off-Balance account can not be reconcilable'))
if record.tax_ids:
raise UserError(_('An Off-Balance account can not have taxes'))
The method decorators help in reducing the code while implementing these useful functionalities.