The Create new records and Update values of a recordset record recipes in Chapter 5, Basic Server Side Development, mentioned that these operations did not call onchange methods automatically. Yet in a number of cases it is important that these operations are called because they update important fields in the created or updated record. Of course, you can do the required computation yourself, but this is not always possible as the onchange method can be added or modified by a third-party addon module installed on the instance that you don't know about.
This recipe explains how to call the onchange methods on a record by manually playing the onchange method before creating a record.
We will reuse the settings from the preceding recipe, Write onchange methods. The action will take place in a new method of library.member
called return_all_books(self)
.
In this recipe, we will manually create a record of the library.returns.wizard
model, and we want the onchange method to compute the returned books for us. To do this, you need to perform the following steps:
return_all_books
in the LibraryMember
class:@api.multi def return_all_books(self): self.ensure_one
library.returns.wizard
:wizard = self.env['library.returns.wizard']
values = {'member_id': self.id, book_ids=False}
specs = wizard._onchange_spec()
updates = wizard.onchange(values, ['member_id'], specs)
value = updates.get('value', {}) for name, val in value.iteritems(): if isinstance(val, tuple): value[name] = val[0] values.update(value)
record = wizard.create(values)
For an explanation of step 1 to step 3, please refer to the recipe Create new records in Chapter 5, Basic Server Side Development.
Step 4 calls the _onchange_spec
method on the model, passing no argument. This method will retrieve the updates that are triggered by the modification of which other field. It does this by examining the form view of the model (remember, onchange methods are normally called by the web client).
Step 5 calls the
onchange(values, field_name, field_onchange)
method of the model with 3 arguments:
values
: The list of values we want to set on the record. You need to provide a value for all the fields you expect to be modified by the onchange method. In the recipe, we set book_ids
to False
for this reason.field_name
: A list of fields for which we want to trigger the onchange methods. You can pass an empty list, and it will use the fields defined in values. However, you will often want to specify that list manually to control the order of evaluation, in case different fields can update a common field.field_onchange
: The onchange specifications that were computed in step 4. This method finds out which onchange methods must be called and in what order and returns a dictionary, which can contain the following keys:value
: This is a dictionary of newly computed field values. This dictionary only features keys that are in the values
parameter passed to onchange()
. Note that Many2one
fields are mapped to a tuple containing (id, display_name)
as an optimization for the web client.warning
: This is a dictionary containing a warning message that the web client will display to the user.domain
: This is a dictionary mapping field names to new validity domains.Generally, when manually playing onchange methods, we only care about what is in value
.
Step 6 updates our initial values dictionary with the values computed by the onchange. We process the values corresponding to Many2one
fields to only keep the id
. To do so, we take advantage of the fact that these fields are only those whose value is returned as a tuple.
Step 7 finally creates the record.
If you need to call an onchange method after modifying a field, the code is the same. You just need to get a dictionary for the values of the record, which can be obtained by using values = dict(record._cache)
after modifying the field.