Defining model methods and use the API decorators

The model classes defining custom data models declare fields for the data processed by the model. They can also define custom behavior by defining methods on the model class.

In this recipe, we will see how to write a method that can be called by a button in the user interface, or by some other piece of code in our application. This method will act on LibraryBooks and perform the required actions to change the state of a selection of books.

Getting ready

This recipe assumes you have an instance ready, with the my_module addon module available, as described in Chapter 3, Creating Odoo Modules. You will need to add a state field to the LibraryBook model defined as follows:

from openerp import models, fields, api
class LibraryBook(models.Model):
    # […]
    state = fields.Selection([('draft', 'Unavailable'),
                              ('available', 'Available'),
                              ('borrowed', 'Borrowed'),
                              ('lost', 'Lost')],
                             'State')

Please refer to Chapter 4, Adding data fields to a Model for more clarity.

How to do it…

For defining a method on LibraryBook to allow changing the state of a selection of books, you need to add the following code to the model definition:

  1. Add a helper method to check whether a state transition is allowed:
        @api.model
        def is_allowed_transition(self, old_state, new_state):
            allowed= [('draft', 'available'),
                      ('available', 'borrowed'),
                      ('borrowed', 'available'),
                      ('available', 'lost'),
                      ('borrowed', 'lost'),
                      ('lost', 'available')]
            return (old_state, new_state) in allowed
  2. Add a method to change the state of some books to a new one passed as an argument:
        @api.multi
        def change_state(self, new_state):
            for book in self:
                if book.is_allowed_transition(book.state,
                                              new_state):
                    book.state = new_state
                else:
                    continue

How it works…

The code in the recipe defines two methods. They are normal Python methods, having self as their first argument and can have additional arguments as well.

The methods are decorated with decorators from the openerp.api module. These decorators are there to ensure the conversion of calls made using the old or traditional API to the new API. The old API is used by the remote procedure call (RPC) protocol of the web client and by modules that have not yet been ported to the new API. A key part of this conversion is the creation of an execution environment stored in self.env; it contains the following:

  • self.env.cr: This is a database cursor
  • self.env.user: This is the user executing the action
  • self.env.context: This is context, which is a Python dictionary containing various information such as the language of the user, his configured time zone, and other specific keys that can be set at run time by the actions of the user interface

When writing a new method, you will generally be using @api.multi. In addition to the creation of the environment, this decorator tells the RPC layer to initialize self using the record IDs supplied in the RPC argument ids. In such methods, self is a recordset that can refer to an arbitrary number of database records.

The @api.model decorator is similar but is used on methods for which only the model is important, not the contents of the recordset, which is not acted upon by the method. The concept is similar to Python's @classmethod decorator (but we generally cannot use this in Odoo models because we need access to self.env and other important instance attributes).

Note

The @api.model and @api.multi decorators also have specific meaning when ensuring the compatibility of your addon module with other modules still using the "old API". You will find more information on this in the Porting old API code to the new API recipe in Chapter 6, Advanced Server Side Development Techniques.

Here is an example code snippet called change_state():

# returned_book_ids is a list of book ids to return
books = self.env['library.book']
books.browse(returned_book_ids).change_state('available')

When change_state() is called, self is a (possibly empty) recordset containing records of the library.book model. The body of the change_state() method loops over self to process each book in the recordset. Looping on self looks strange at first, but you will get used to this pattern very quickly.

Inside the loop, change_state() calls is_allowed_transition(). The call is made using the local variable book, but it could have been made on any recordset for the library.book model, including, for example, self, since is_allowed_transition() is decorated with @api.model. If the transition is allowed, change_state() assigns the new state to the book by assigning a value to the attribute of the recordset. This is only valid on recordsets of length 1, which is guaranteed to be the case when iterating over self.

There's more…

There are some important factors that are worth understanding.

Hiding methods from the RPC interface

In the old API, methods with a name prefixed by an underscore are not exposed through the RPC interface and are therefore not callable by the web client. This is still the case with the new API, which also offers another way to make a method unavailable to the RPC interface; if you don't put an @api.model or @api.multi decorator on it (or on one of the decorators mentioned in the Porting old API code to the new API recipe in Chapter 6, Advanced Server Side Development Techniques), then the method will neither be callable by extensions of the model using the traditional API nor via RPC.

The @api.one decorator

You may encounter the @api.one decorator while reading source code. In Odoo 9.0, this decorator is deprecated because its behavior can be confusing—at first glance, and knowing of @api.multi, it looks like this decorator allows the method to be called only on recordsets of size 1, but it does not. When it comes to recordset length, @api.one is similar to @api.multi, but it does a for loop on the recordset outside the method and aggregates the returned value of each iteration of the loop in a list, which is returned to the caller. Avoid using it in your code.

See also

In Chapter 8, Backend Views, refer to the Adding buttons to form recipe to learn how to call such a method from the user interface.

You will find additional information on the decorators defined in openerp.api in the recipe from Chapter 6, Advanced Server Side Development Techniques: Porting old API code to the new API.

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

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