Odoo API
Odoo API
Odoo API
Goal
Make the model API
more object-oriented
more Pythonic
less cluttered
From V7 to V8
compatibility: V7 and V8 APIs are interoperable
Agenda
Recordsets
Methods
Environment
Fields
Migrating Modules
Recordsets
Programming with
Recordsets
A recordset is:
an ordered collection of records
one concept to replace
browse records,
browse lists,
browse nulls.
an instance of the model's class
# another record
partners.
.name = 'Agrolait'
partners[
[0].name = 'Agrolait'
# False
# empty recordset
partners.
.name = 'Foo'
# ERROR
Methods
Method decorators
Decorators enable support of both old and new API:
from odoo import Model,
, api
class stuff(
(Model):
):
@api.model
def create(
(self,
, values):
):
# self is a recordset, but its content is unused
...
Method decorators
from odoo import Model,
, api
class stuff(
(Model):
):
@api.multi
def write(
(self,
, values):
):
# self is a recordset and its content is used
# update self.ids
...
Method decorators
One-by-one or "autoloop" decorator:
from odoo import Model,
, api
class stuff(
(Model):
):
@api.one
def cancel(
(self):
):
self.
.state = 'cancel'
Method decorators
Methods that return a recordset instead of ids:
from odoo import Model,
, api
class stuff(
(Model):
):
@api.multi
@api.returns
@api.returns(
('res.partner')
)
def root_partner(
(self):
):
p = self.
.partner_id
while p.
.parent_id:
:
p = p.
.parent_id
return p
# uid as a record
recs.
.env.
.ref(
('base.group_user')
)
# resolve xml id
recs.
.env[
['res.partner']
]
# uid = SUPERUSER_ID
Fields
Fields as descriptors
Python descriptors provide getter/setter (like property):
from odoo import Model,
, fields
class res_partner(
(Model):
):
_name = 'res.partner'
name = fields.
.Char(
(required=
=True)
)
parent_id = fields.
.Many2one(
('res.partner',
, string=
='Parent')
)
Computed fields
Regular elds with the name of the compute method:
class res_partner(
(Model):
):
...
display_name = fields.
.Char(
(
string=
='Name',
, compute=
='_compute_display_name',
,
)
@api.one
@api.depends
@api.depends(
('name',
, 'parent_id.name')
)
def _compute_display_name(
(self):
):
names = [self.
.parent_id.
.name,
, self.
.name]
]
self.
.display_name = ' / '.
.join(
(filter(
(None,
, names))
))
Computed fields
The compute method must assign eld(s) on records:
untaxed = fields.
.Float(
(compute=
='_amounts')
)
taxes = fields.
.Float(
(compute=
='_amounts')
)
total = fields.
.Float(
(compute=
='_amounts')
)
@api.multi
@api.depends
@api.depends(
('lines.amount',
, 'lines.taxes')
)
def _amounts(
(self):
):
for order in self:
:
order.
.untaxed = sum(
(line.
.amount for line in order.
.lines)
)
order.
.taxes = sum(
(line.
.taxes for line in order.
.lines)
)
order.
.total = order.
.untaxed + order.
.taxes
Computed fields
Stored computed elds are much easier now:
display_name = fields.
.Char(
(
string=
='Name',
, compute=
='_compute_display_name',
, store=
=True,
,
)
@api.one
@api.depends
@api.depends(
('name',
, 'parent_id.name')
)
def _compute_display_name(
(self):
):
...
Onchange methods
For computed elds: nothing to do!
For other elds: API is similar to compute methods:
@api.onchange
@api.onchange(
('partner_id')
)
def _onchange_partner(
(self):
):
if self.
.partner_id:
:
self.
.delivery_id = self.
.partner_id
Onchange methods
A eld element on a form is automatically decorated with
on_change="1":
if it has an onchange method
if it is a dependency of a computed eld
This mechanism may be prevented by explicitly decorating
a eld element with on_change="0".
Python constraints
Similar API, with a specic decorator:
@api.one
@api.constrains
@api.constrains(
('lines',
, 'max_lines')
)
def _check_size(
(self):
):
if len(
(self.
.lines)
) > self.
.max_lines:
:
raise Warning
Warning(
(_(
("Too many lines in %s")
) % self.
.name)
)
Migrating Modules
Guidelines
Do the migration step-by-step:
1. migrate eld denitions
rewrite compute methods
2. migrate methods
rely on interoperability
use decorators, they are necessary
3. rewrite onchange methods (incompatible API)
beware of overridden methods!