When working with a recordset of length 1
, the various fields are available as record attributes. Relational attributes (One2many
, Many2one
, and Many2many
) are also available with values that are recordsets too. When working with recordsets with more than one record, the attributes cannot be used.
This recipe shows how to use the mapped()
method to traverse recordset relations; we will write two methods performing the following operations:
We will be reusing the simplified Partner model shown in the Create new records recipe of this chapter.
To write the partner manipulation methods, you need to perform the following steps:
get_email_addresses()
:@api.model def get_email_addresses(self, partner): partner.ensure_one()
mapped()
to get the e-mail addresses of the contacts of the partner:return partner.mapped('child_ids.email')
get_companies()
:@api.model def get_companies(self, partners):
mapped()
to get the different companies of the partners:return partners.mapped('parent_id')
In step 1, we call ensure_one()
to make sure we have a single partner. This is not required for the recipe, as mapped()
works very well on recordsets of arbitrary size. However, it is mandated by the specification of the method we are writing, as we don't want to retrieve contacts from multiple companies by mistake.
In step 2 and step 4, we call the mapped(path)
method to traverse the fields of the recordset; path
is a string containing field names separated by dots. For each field in the path, mapped()
produces a new recordset containing all the records related by this field to all elements in the current recordset and then applies the next element in the path on that new recordset. If the last field in the path is a relational field, mapped()
will return a recordset; otherwise, a Python list is returned.
The mapped()
method has two remarkable properties:
This second property is very useful in a method decorated with @api.multi
where you want to perform an operation on all the records pointed by a Many2many
field of all records in self
, but need to make sure that the action is performed only once (even if two records of self share the same target record).
When using mapped()
, keep in mind that it operates in the memory inside the Odoo server by repeatedly traversing relations and therefore making SQL queries, which may not be efficient; however, the code is terse and expressive. If you are trying to optimize a method on the critical path of the performance of your instance, you may want to rewrite the call to mapped()
and express it as a search()
with the appropriate domain, or even move to SQL (at the cost of readability).
The mapped()
method can also be called with a function as argument. In this case, it returns a list containing the result of the function applied to each record of self
, or the union of the recordsets returned by the function, if the function returns a recordset.