Adding computed fields to a Model

Sometimes, we need to have a field that has a value calculated or derived from other fields in the same record or in related records. A typical example is the total amount that is calculated by multiplying a unit price with a quantity. In Odoo models, this can be achieved using computed fields.

To show how computed fields work, we will add one to the Library Books model to calculate the days since the book's release date.

It is also possible to make computed fields editable. We will also implement this in our example.

Getting ready

We will reuse the my_module addon module from Chapter 3, Creating Odoo Modules.

How to do it…

We will edit the models/library_book.py code file to add a new field and the methods supporting its logic:

  1. Start by adding the new field to the Library Books model:
    class LibraryBook(models.Model):
        # ...
        age_days = fields.Float(
            string='Days Since Release',
            compute='_compute_age',
            inverse='_inverse_age',
            search='_search_age',
            store=False,
            compute_sudo=False,
        )
  2. Next, add the method with the value computation logic:
    # ...
    from openerp import api  # if not already imported
    from openerp.fields import Date as fDate
    # ...
    class LibraryBook(models.Model):
        # …
        @api.depends('date_release')
        def _compute_age(self):
            today = fDate.from_string(fDate.today())
            for book in self.filtered('date_release'):
                delta = (fDate.from_string(book.date_release - today)
                book.age_days = delta.days
    
  3. To add the method implementing the logic to write on the computed field, use the following code:
    from datetime import timedelta as td
    # ...
    class LibraryBook(models.Model):
        # …
        def _inverse_age(self): today = fDate.from_string(fDate.today())
            for book in self.filtered('date_release'):
                d = td(days=book.age_days) - today
                book.date_release = fDate.to_string(d)
    
  4. To implement the logic allowing you to search on the computed field, use the following code:
    # from datetime import timedelta as td
    class LibraryBook(models.Model):
        # …
        def _search_age(self, operator, value):
            today = fDate.from_string(fDate.today())
            value_days = td(days=value)
            value_date = fDate.to_string(today - value_days)
            return [('date_release', operator, value_date)]
    

An Odoo restart followed by a module upgrade should be needed to correctly activate these new additions.

How it works…

The definition of a computed field is the same as the one for a regular field, except that a compute attribute is added to specify the name of the method to use for its computation.

The similarity can be deceptive, since computed fields are internally quite different from regular fields. Computed fields are dynamically calculated at runtime, and unless you specifically add that support yourself, they are not writeable or searchable.

The computation function is dynamically calculated at runtime, but the ORM uses caching to avoid inefficiently recalculating it every time its value is accessed. So, it needs to know what other fields it depends on, using the @depends decorator to detect when its cached values should be invalidated and recalculated.

Note

Make sure that the compute function always sets a value on the computed field. Otherwise, an error will be raised. This can happen when you have if conditions in your code that sometimes fail to set a value on the computed field, and can be tricky to debug.

Write support can be added by implementing the inverse function; it uses the value assigned to the computed field to update the origin fields. Of course, this only makes sense for simpler calculations; nevertheless, there are still cases where it can be useful. In our example, we make it possible to set the book release date by editing the Days Since Release computed field.

It is also possible to make searchable a non-stored computed field by setting the search attribute to the method name to use (similar to compute and inverse).

However, this method is not expected to implement the actual search. Instead, it receives as parameters the operator and value used to search on the field and is expected to return a domain with the replacement search conditions to use. In our example, we translate a search of the Days Since Release field into an equivalent search condition on the Release Date field.

The optional store=True flag makes the field stored in the database. In this case, after being computed, the field values are stored in the database, and from there on, they are retrieved like regular fields instead of being recomputed at runtime. Thanks to the @api.depends decorator, the ORM will know when these stored values need to be recomputed and updated. You can think of it as a persistent cache. It also has the advantage of making the field usable for search conditions, sorting and grouping by operations, without the need to implement the search method.

The compute_sudo=True flag is to be used in those cases where the computations need to be done with elevated privileges. This can be the case when the computation needs to use data that may not be accessible to the end user.

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

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