Extending the business logic defined in a Model

When defining a model that extends another model, it is often necessary to customize the behavior of some methods defined on the original model. This is a very easy task in Odoo, and one of the most powerful features of the underlying framework.

We will demonstrate this by extending a method that creates records to add a new field in the created records.

Getting ready

If you want to follow the recipe, make sure you have the my_module addon from Chapter 3, Creating Odoo Modules, with the loan wizard defined in the Writing a wizard to guide the user recipe from Chapter 6, Advanced Server Side Development Techniques.

Create a new addon module called library_loan_return_date that depends on my_module. In this module, extend the library.book.loan model as follows:

class LibraryBookLoan(models.Model):
    _inherit = 'library.book.loan'
    expected_return_date = fields.Date('Due for', required=True)

Extend the library.member model as follows:

class LibraryMember(models.Model):
    _inherit = 'library.member'
    loan_duration = fields.Integer('Loan duration',
                                   default=15,
                                   required=True)

Since the expected_return_date field is required and no default is provided, the wizard to record loans will cease functioning because it does not provide a value for that field when it creates loans.

How to do it…

To extend the business logic in the library.loan.wizard model, you need to perform the following steps:

  1. In my_module, modify the method record_loans() in the LibraryLoanWizard class. The original code is in the Writing a wizard to guide the user recipe in Chapter 6, Advanced Server Side Development Techniques. The new version is:
        @api.multi
        def record_loans(self):
            for wizard in self:
                books = wizard .book_ids
                loan = self.env['library.book.loan']
                for book in wizard.book_ids:
                    values = self._prepare_loan(book)
                    loan.create(values)
         @api.multi
         def _prepare_loan(self, book):
             return {'member_id': self.member_id.id,
                     'book_id': book.id}
  2. In library_loan_return_date, create a class which extends library.loan.wizard and defines the _prepare_loan method as follows:
    from datetime import timedelta
    from openerp import models, fields, api
    class LibraryLoanWizard(models.TransientModel):
        _inherit = 'library.load.wizard'
    
        def _prepare_loan(self, book):
            values = super(LibraryLoanWizard,
                           self
                           )._prepare_loan(book)
            loan_duration = self.member_id.loan_duration
            today_str = fields.Date.context_today()
            today = fields.Date.from_string(today_str)
            expected = today + timedelta(days=loan_duration)
            values.update(
                {'expected_return_date':
                     fields.Date.to_string(expected)}
            )
            return values

How it works…

In step 1, we refactor the code from the Writing a wizard to guide the user recipe in Chapter 6, Advanced Server Side Development Techniques, to use a very common and useful coding pattern to create the library.book.loan records: the creation of the dictionary of values is extracted in a separate method rather than hardcoded in the method calling create(). This eases extending the addon module in case new values have to be passed at creation time, which is exactly the case we are facing.

Step 2 then does the extension of the business logic. We define a model that extends library.loan.wizard and redefines the _prepare_loan() method. The redefinition starts by calling the implementation from the parent class:

values = super(LibraryLoanWizard, self)._prepare_loan(book)

In the case of Odoo models, the parent class is not what you'd expect by looking at the Python class definition. The framework has dynamically generated a class hierarchy for our recordset, and the parent class is the definition of the model from the modules on which we depend. So the call to super() brings back the implementation of library.loan.wizard from my_module. In this implementation, _prepare_loan() returns a dictionary of values with 'member_id' and 'book_id'. We update this dictionary by adding the 'expected_return_date' key before returning it.

There's more…

In this recipe, we chose to extend the behavior by calling the normal implementation and modifying the returned result afterwards. It is also possible to perform some actions before calling the normal implementation, and of course, we can also do both.

However, what we saw in this recipe is that it is harder to change the behavior of the middle of a method. We had to refactor the code to extract an extension point to a separate method and override this new method in the extension module.

Note

You may be tempted to completely rewrite a method. Always be very cautious when doing so—if you do not call the super() implementation of your method, you are breaking the extension mechanism and potentially breaking addons for which their extension of the same method will never be called. Unless you are working in a controlled environment, in which you know exactly which addons are installed and you've checked that you are not breaking them, avoid doing this. And if you have to, be sure to document what you are doing in a very visible way.

What can you do before and after calling the original implementation of the method? Lots of things, including (but not limited to):

  • Modifying the arguments that are passed to the original implementation (before)
  • Modifying the context that is passed to the original implementation (before)
  • Modifying the result that is returned by the original implementation (after)
  • Calling another method (before, after)
  • Creating records (before, after)
  • Raising a UserError to cancel the execution in forbidden cases (before, after)
  • Splitting self in smaller recordsets, and calling the original implementation on each of the subsets in a different way (before)
..................Content has been hidden....................

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