Model Methods and Decorators
In Odoo models, the class includes field declarations and business logic methods. One
simple type of method is the method defined for a button click.
For example,confirm button in a form view changes the state from ‘Draft’ to ‘Confirmed’
The function will be declared from the XML first :
button name="action_confirm" string="Confirm" type="object"/
The form view will have a ‘Confirm’ button, its python action is declared in the button
‘name’ attribute(action_confirm).
state=fields.Selection([(draft,'Draft'),(confirmed,'Confirmed')],default='draft')
def action_confirm(self):
self.state = 'confirmed'
Here, action_confirm is a python method with self as an argument. ‘self’, the first
argument method of odoo models can also have additional arguments.
Decorators.
Decorators allow you to change the behavior of a method. Simply it adds some capability
to a function and then returns it.
@api.autovaccum
The daily vacuum cron job calls the methods decorated by this decorator (model
ir.autovacuum).This is used for tasks that do not require a particular cron job.
@api.constrains(*args)
The arguments passed to the decorator are fields from the specific model.
The function will get called when any of the fields are changed.
@constrains only support field names, not dotted fields(related fields,
Eg:partner_id.city).
Only if the defined fields in the decorated method are included in the model's create or
write call will invoked by @constrains. It means that fields that do not exist in a view
will not cause the function to be called during record generation.
Override of create is required to ensure the constraint is always activated.
To ensure that a constraint is always activated, an override of create is required.
@api.depends
The field dependencies of the “compute” method can be declared with this decorator. Each
argument must be a string consisting of a dot-separated sequence 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()
else:
record.pname = record.partner_id.name
@api.depends_context(*args)
The context dependencies of a non stored “compute” method can be declared with this
decorator. Each argument passed is a key in the context dictionary.
price = fields.Float(compute='_compute_product_price')
@api.depends_context('pricelist')
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'])
else:
pricelist = self.env['product.pricelist'].get_default_pricelist()
product.price = pricelist.get_products_price(product).get(product.id, 0.0)
@api.model
Decorate a record-style method where self is a recordset, but its contents is not
relevant, only the model is.
@api.model
def method(self, args):
//Add your method here
@api.model_create_multi
Decorate a method if the method is creating multiple records with list of dictionaries.
It can be called with sing or list of dictionaries.
record = model.create(vals)
records = model.create([vals, ...])
@api.onchange(*args)
Fields in the view will be passed as arguments to the ‘onchange’ decorator. When the
field from the view is modified, the method will be called. The method is invoked on a
pseudo-record that contains the values present in the form. Field assignments on that
record are automatically sent back to the client.
@api.onchange('partner_id')
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)
Since @onchange returns a recordset of pseudo-records, calling any one of the CRUD
methods (create(), read(), write(), unlink()) on this recordset is undefined behaviour,
as they potentially do not exist in the database yet. Instead of it, we can use the
update() method to set the record’s fields or simply set the field as in the example.
An one2many or many2many field can't modify itself via onchange.
@api.ondelete(*, at_uninstall)
Mark a method to be executed during unlink().
Methods decorated with @ondelete should raise an error following some conditions, and by
convention, the method should be named either _unlink_if_<condition> or _unlink_except_
<not_condition>.
@api.ondelete(at_uninstall=False)
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
@api.ondelete(at_uninstall=False)
def _unlink_except_active_user(self):
if any(user.active for user in self):
raise UserError("Can't delete an active user!")
Parameters:
at_uninstall (bool) – This decorated method will be called when the
module which implements this function is uninstalled. It should almost always be False,
so that module uninstallation does not trigger those errors.
@api.returns(model, downgrade=None, upgrade=None)
For methods that return model instances, return a decorator.
Parameters
- model – A model name, or 'self' for the current model
- downgrade – A function downgrade(self, value, *args, **kwargs) to convert the
record-style value to a traditional-style output
- upgrade – A function upgrade(self, value, *args, **kwargs) to convert the
traditional-style value to a record-style output
In the record-style, the parameters self, *args, and **kwargs are passed to the method
The decorator adjusts the output of the method to the api style: id, ids, or False for
the traditional style, and recordset for the record style.
@model
@returns('res.partner')
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)
Note that the decorated method must satisfy that convention.
Those decorators are automatically inherited: A method that overrides a decorated
existing method will be decorated with the same @returns(model).