Computed fields

Fields can have values calculated by a function, instead of simply reading a database stored value. A computed field is declared just like a regular field, but has an additional argument compute with the name of the function used to calculate it.

In most cases computed fields involve writing some business logic, so we will develop this topic more in Chapter 7, ORM Application Logic - Supporting Business Processes. We can still explain them here, but keeping the business logic side as simple as possible.

Let's work on an example: stages have a fold field. We will add to tasks a computed field with the Folded? flag for the corresponding stage.

We should edit the TodoTask model in the todo_ui/todo_model.py file to add the following:

# class TodoTask(models.Model):
    stage_fold = fields.Boolean(
        'Stage Folded?',
        compute='_compute_stage_fold')

    @api.one
    @api.depends('stage_id.fold')
    def _compute_stage_fold(self):
        self.stage_fold = self.stage_id.fold

The preceding code adds a new stage_fold field and the _compute_stage_fold method used to compute it. The function name was passed as a string, but it's also allowed to pass it as a callable reference (the function identifier with no quotes).

Since we are using the @api.one decorator, self will represent a single record. If we used @api.multi instead, it would represent a recordset and our code would need to handle the iteration over each record.

The @api.depends is necessary if the computation uses other fields: it tells the server when to recompute stored or cached values. It accepts one or more field names as arguments and dot-notation can be used to follow field relations.

The computation function is expected to assign a value to the field or fields to compute. If it doesn't, it will error. Since self is a record object, our computation is simply to get the Folded? field using self.stage_id.fold. The result is achieved by assigning that value (writing it) to the computed field, self.stage_fold.

We won't be working yet on the views for this module, but you can make a quick edit on the task form to confirm if the computed field is working as expected: using the Developer Menu pick the Edit View option and add the field directly in the form XML. Don't worry: it will be replaced by the clean module view on the next upgrade.

Search and write on computed fields

The computed field we just created can be read, but it can't be searched or written. This can be enabled by providing specialized functions for that. Along with the compute function, we can also set a search function, implementing the search logic, and the inverse function, implementing the write logic.

In order to do this, our computed field declaration becomes like this:

# class TodoTask(models.Model):
    stage_fold = fields.Boolean(
        string='Stage Folded?',
        compute='_compute_stage_fold',
        # store=False)  # the default
        search='_search_stage_fold',
        inverse='_write_stage_fold')

The supporting functions are:

    def _search_stage_fold(self, operator, value):
        return [('stage_id.fold', operator, value)]

    def _write_stage_fold(self):
        self.stage_id.fold = self.stage_fold

The search function is called whenever a (field, operator, value) condition on this field is found in a search domain expression. It receives the operator and value for the search and is expected to translate the original search element into an alternative domain search expression.

The inverse function performs the reverse logic of the calculation, to find the value to write on the source fields. In our example, it's just writing on stage_id.fold.

Storing computed fields

Computed field's values can also be stored on the database, by setting store to True on their definition. They will be recomputed when any of their dependencies change. Since the values are now stored, they can be searched just like regular fields, so a search function is not needed.

Related fields

The computed field we implemented in the previous section is a special case that can be automatically handled by Odoo. The same effect can be achieved using Related fields. They make available, directly on a model, fields that belong to a related model, accessible using a dot-notation chain. This makes them usable in situations where dot-notation can't be used, such as UI forms.

To create a related field, we declare a field of the needed type, just like with regular computed fields, and instead of compute, use the related attribute indicating the dot-notation field chain to reach the desired field.

To-do tasks are organized in customizable stages and these is turn map into basic states. We will make them available on tasks, and will use this for some client-side logic in the next chapter.

Similarly to stage_fold, we will add a computed field on the task model, but now using the simpler Related field:

# class TodoTask(models.Model):
    stage_state = fields.Selection(
        related='stage_id.state',
        string='Stage State')

Behind the scenes, Related fields are just computed fields that conveniently implement search and inverse. This means that we can search and write on them out of the box, without having to write any additional code.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset