Looking again at our module design, we have these relationships:

The following Entity Relationship Diagram can help visualizing the relationships we are about to create on the model. The lines ending with a triangle represent a many sides of the relationships:

Relationships between models

Let's add the corresponding relationship fields to the to-do tasks in our todo_model.py file:

class TodoTask(models.Model): 
    _inherit = 'todo.task' 
    stage_id = fields.Many2one('todo.task.stage', 'Stage') 
    tag_ids = fields.Many2many('todo.task.tag', string='Tags') 

The preceding code shows the basic syntax of these fields, setting the related model and the field's title string. The convention for relational field names is to append _id or _ids to the field names, for to-one and to-many relationships, respectively. 

As an exercise, you may try to also add the corresponding inverse relationships to the related models:

Let's have a closer look at relational field definitions.

The Many2many minimal signature accepts one argument for the related model, and it is recommended to also provide the string argument with the field title.

At the database level, it does not add any columns to the existing tables. Instead, it automatically creates a new relationship table that has only two ID fields with the foreign keys for the related tables. The relationship table name and the field names are automatically generated. The relationship table name is the two table names joined with an underscore with _rel appended to it.

On some occasions we may need to override these automatic defaults.

One such case is when the related models have long names, and the name for the automatically generated relationship table is too long, exceeding the 63 characters PostgreSQL limit. In these cases we need to manually choose a name for the relationship table, to conform to the table name size limit.

Another case is when we need a second many-to-many relationship between the same models. In these cases we need to manually provide a name for the relationship table, so that it doesn't collide with the table name already being used for the first relationship.

There are two alternatives to manually override these values: either using positional arguments or keyword arguments.  

Using positional arguments for the field definition we have:

# Task <-> Tag relation (positional args): 
tag_ids = fields.Many2many( 
    'todo.task.tag',      # related model 
    'todo_task_tag_rel',  # relation table name 
    'task_id',            # field for "this" record 
    'tag_id',             # field for "other" record 
    string='Tags') 

We can instead use keyword arguments, which some people prefer for readability:

# Task <-> Tag relation (keyword args): 
tag_ids = fields.Many2many( 
    comodel_name='todo.task.tag',  # related model 
    relation='todo_task_tag_rel',# relation table name 
    column1='task_id',      # field for "this" record 
    column2='tag_id',       # field for "other" record 
    string='Tags') 

Just like many-to-one fields, many-to-many fields also support the domain and context keyword attributes.

The inverse of the Many2many relationship is also a Many2many field. If we also add a Many2many field to the Tags model, Odoo infers that this many-to-many relationship is the inverse of the one in the Task model.

The inverse relationship between Tasks and Tags can be implemented like this:  

class Tag(models.Model):    
    _name = 'todo.task.tag'   
    # Tag class relationship to Tasks:    
    task_ids = fields.Many2many(       
        'todo.task',    # related model        
        string='Tasks')

Parent-child tree relationships are represented using a Many2one relationship with the same model, so that each record references its parent. And the inverse One2many makes it easy for a parent to keep track of its children.

Odoo provides improved support for these hierarchic data structures, for faster browsing through tree siblings, and for easier search using the additional child_of operator in domain expressions.

To enable these features we need to set the _parent_store flag attribute and add to the model the helper fields: parent_left and parent_right. Mind that this additional operation comes at storage and execution time penalties, so it's best used when you expect to read more frequently than write, such as a the case of a category tree.

Revisiting the Tags model, defined in the todo_model.py file, we should now edit it to look like this:

class Tags(models.Model):
    _name = 'todo.task.tag'
    _description = 'To-do Tag'
    _parent_store = True
    # _parent_name = 'parent_id'
    name = fields.Char('Name')
    parent_id = fields.Many2one(

       'todo.task.tag', 'Parent Tag', ondelete='restrict')

    parent_left = fields.Integer('Parent Left', index=True)

    parent_right = fields.Integer('Parent Right', index=True)

Here, we have a basic model, with a parent_id field to reference the parent record, and the additional _parent_store attribute to add hierarchic search support. When doing this, the parent_left and parent_right fields must also be added.

The field referring to the parent is expected to be named parent_id, but any other field name can be used as long as we declare that in the _parent_name attribute.

Also, it is often convenient to add a field with the direct children of the record:

child_ids = fields.One2many(
    'todo.task.tag', 'parent_id', 'Child Tags')

Regular relational fields reference one fixed comodel. The Reference field type does not have this limitation and supports dynamic relationships, so that the same field is able to refer to more than one model.

For example, we can use it to add a Refers to field to To-do Tasks, that can either refer to a User or a Partner:

# class TodoTask(models.Model):
    refers_to = fields.Reference(
    [('res.user', 'User'), ('res.partner', 'Partner')],
    'Refers to')

As you can see, the field definition is similar to a Selection field, but here the selection list holds the models that can be used. On the user interface, the user will first pick a model from the av available list, and then pick a record from that model.

This can be taken to another level of flexibility: a Referenceable Models configuration table exists to configure the models that can be used in Reference fields. It is available in the Settings | Technical | Database Structure menu. When creating such a field we can set it to use any model registered there, with the help of the referenceable_models() function in the odoo.addons.res.res_request module.

Using the Referenceable Models configuration, an improved version of the Refers to field would look like this:

from odoo.addons.base.res.res_request import referenceable_models
# class TodoTask(models.Model):
    refers_to = fields.Reference(

        referenceable_models, 'Refers to')

Note that in Odoo 9.0 this function used a slightly different spelling, and was still using the old API. So in version 9.0, before using the code shown before, we have to add some code at the top of our Python file to wrap it so that it uses the new API:

from openerp.addons.base.res import res_request
    def referenceable_models(self):
        return res_request.referencable_models( 
            self, self.env.cr, self.env.uid, context=self.env.context)