New models are defined through Python classes. Extending them is also done through Python classes, but with the help of an Odoo-specific inheritance mechanism.

To extend an existing model, we use a Python class with a _inherit attribute. This identifies the model to be extended. The new class inherits all the features of the parent Odoo model, and we only need to declare the modifications we want to introduce.

In fact, Odoo models exist outside our particular Python module, in a central registry. This registry, can be accessed from model methods using self.env[<model name>]. For example, to get a reference to the object representing the res.partner model, we would write self.env['res.partner'].

To modify an Odoo model, we get a reference to its registry class and then perform in-place changes on it. This means that these modifications will also be available everywhere else where this new model is used.

During the Odoo server startup, the module loading the sequence is relevant: modifications made by one add-on module will only be visible to the add-on modules loaded afterward. So it's important for the module dependencies to be correctly set, ensuring that the modules providing the models we use are included in our dependency tree.

We will extend the todo.task model to add a couple of fields to it: the user responsible for the task and a deadline date.

The coding style guidelines recommended  having a models/ subdirectory with one file per Odoo model. So we should start by creating the model subdirectory, making it Python-importable.

Edit the todo_user/__init__.py file to have this content:

from .import models 

Create todo_user/models/__init__.py with the following code:

from . import todo_task 

The preceding line directs Python to look for a file called odoo_task.py in the same directory and imports it. You would usually have a from line for each Python file in the directory:

Now create the todo_user/models/todo_task.py file to extend the original model:

# -*- coding: utf-8 -*- 
from odoo import models, fields, api 
class TodoTask(models.Model): 
    _inherit = 'todo.task' 
    user_id = fields.Many2one('res.users', 'Responsible') 
    date_deadline = fields.Date('Deadline') 

The class name TodoTask is local to this Python file and, in general, is irrelevant for other modules. The _inherit class attribute is the key here: it tells Odoo that this class is inheriting and thus modifying the todo.task model.

The next two lines are regular field declarations. The user_id field represents a user from the users model res.users. It's a Many2one field, which is equivalent to a foreign key in database jargon. The date_deadline is a simple date field. In Chapter 5Models - Structuring the Application Data, we will explain the types of fields available in Odoo in more detail.

To have the new fields added to the model's supporting database table, we need to perform a module upgrade. If everything goes as expected, you should see the new fields when inspecting the todo.task model in the Technical Database Structure | Models menu option.

Inheritance also works at the business logic level. Adding new methods is simple: just declare their functions inside the inheriting class.

To extend or change the existing logic, the corresponding method can be overridden by declaring a method with the exact same name. The new method will replace the previous one, and it can also just extend the code of the inherited class, using Python's super() method to call the parent method. It can then add new logic around the original logic both before and after super() method is called.

The original Clear All Done action is not appropriate for our task-sharing module anymore since it clears all the tasks, regardless of their user. We need to modify it so that it clears only the current user tasks.

For this, we will override (or replace) the original method with a new version that first finds the list of completed tasks for the current user and then inactivates them:

@api.multi 
def do_clear_done(self): 
    domain = [('is_done', '=', True), 
               '|', ('user_id', '=', self.env.uid), 
                    ('user_id', '=', False)] 
    dones = self.search(domain) 
    dones.write({'active': False}) 
    return True 

For clarity, we first build the filter expression to be used to find the records to be cleared.

This filter expression follows an Odoo-specific syntax referred to as domain: it is a list of conditions, where each condition is a tuple.

These conditions are implicitly joined with the AND (&) operator. For the OR operation, a pipe, |, is used in the place of a tuple, and it joins the next two conditions. We will go into more details about domains in Chapter 6 , Views - Designing the User Interface.

The domain used here filters all the done tasks ('is_done', '=', True) that either have the current user as responsible ('user_id', '=', self.env.uid) or don't have a current user set ('user_id', '=', False).

We then use the search method to get a recordset with the done records to act upon and, finally, do a bulk write on them setting the active field to False. The Python False value here represents the database NULL value.

In this case, we completely overwrote the parent method, replacing it with a new implementation, but that is not what we usually want to do. Instead, we should extend the existing logic with some additional operations. Otherwise, we might break the already existing features.

To have the overriding method keep the already existing logic, we use Python's super() construct to call the parent's version of the method. Let's see an example of this.

We can improve the do_toggle_done() method so that it only performs its action on the tasks assigned to the current user. This is the code to achieve that:

from odoo.exceptions import ValidationError  
# ...
# class TodoTask(models.Model):
# ...
@api.multi 
def do_toggle_done(self): 
    for task in self: 
        if task.user_id != self.env.user: 
            raise ValidationError(
                'Only the responsible can do this!') 
    return super(TodoTask, self).do_toggle_done() 

The method in the inheriting class starts with a for loop to check that none of the tasks to toggle belongs to another user. If these checks pass, it then goes on calling the parent class method, using super(). If not an error is raised, and we should use for this the Odoo built-in exceptions. The most relevant are ValidationError, used here, and UserError.

These are the basic techniques for overriding and extending business logic defined in model classes. Next, we will see how to extend the user interface views.