In the case of relationships in Odoo, it’s not always the case that the connection between two models is static. Even though field types like Many2one fit perfectly for situations where you need to associate two models, the truth is that real-life situations may vary from one to another.
At times, it will be necessary to establish associations between a particular field and many record types according to certain criteria. For these kinds of cases, Odoo offers the Reference and Many2OneReference fields. Let’s look at them in more detail here in this post.
Where to Place the Code (Very Important)
Your custom module should look like this:
your_module_name
+-- __init__.py
+-- __manifest__.py
+-- models/
¦ +-- __init__.py
¦ +-- personal_details.py <-- MODEL CODE HERE
+-- views/
¦ +-- personal_details_views.xml <-- VIEW CODE HERE
reference_doc = fields.Reference(
selection=[
('sale.order', 'Sales Order'),
('account.move', 'Invoice'),
('stock.picking', 'Delivery Order'),
],
string="Related Document",
help="Link this ticket to a business document."
)
Reference Fields in Odoo 18
The Reference field allows the user to establish relationships between a record and several models in just one field.
No need to declare several fields; only define the list of acceptable models, and the user will select one while working.
How It Works
Working with a Reference field in the frontend:
- Firstly, the user chooses the model (Sales Order, Invoice etc.)
- Then the user selects a specific record in this model.
- Data storage in Odoo is done internally by the database.
Example value stored:
sale.order, 24
Explanation:
- Model > sale.order
- Record ID > 24
Advantages
- Fewer field requirements
- Flexible to use
- Good for general references (logs, documents, activities etc.)
Important Notes
- No foreign key relationship established
- selection_add usage possible
- Slightly slow as compared to Many2one because it is dynamic, Control what models are allowed via the selection
Many2oneReference Fields in Odoo 18
The Many2oneReference field is yet another type of field used to create dynamic relationships. Unlike the previous method, the Many2oneReference field separates the whole idea into two parts:
- A Char field > stores the model name
- A Many2oneReference field > stores the record ID
How It Works
- The user enters the model name (for example, sale.order), Accordingly, the field of record changes itself depending on the value provided
- Finally, the user can select the required record benefits
- More control over the process of creating dynamic relationships
- Separates the model from the record
- Used when developing a configurable system
This makes the field behave dynamically depending on the model value.
Why Use It
- Gives more control over dynamic relationships
- Cleaner separation of model and record
- Useful in configurable systems
model_name = fields.Char(
string="Model Name",
help="Technical model name (e.g., sale.order, purchase.order)"
)
record_id = fields.Many2oneReference(
string="Record",
model_field='model_name',
help="Select record dynamically based on model."
)
Key Differences
| Feature | Reference | Many2oneReference |
| Storage | Single field | Two fields |
| Model control | Predefined list | Dynamic |
| Flexibility | Medium | High |
| Simplicity | Easier | Slightly complex |
When Should You Use These Fields?
Use Reference Field When:
- You know the possible models in advance
- You want a simple dropdown-based selection
Example:
- Linking a record to Sale Order / Invoice / Delivery
Use Many2oneReference When:
- The model is not fixed
- The model should be dynamic or configurable
Example:
- Workflow engines
- Approval systems
- Generic automation rules
Complete Model Code (Odoo 18)
Create a file named personal_details.py inside the models directory.
# -*- coding: utf-8 -*-
from odoo import fields, models
class PersonalDetails(models.Model):
_name = 'personal.details'
_description = 'Personal Details'
name = fields.Char(
string='Name',
required=True,
help="Enter the full name of the person."
)
age = fields.Integer(
string='Age',
help="Enter the age."
)
gender = fields.Selection(
[
('male', 'Male'),
('female', 'Female'),
('other', 'Other')
],
string='Gender'
)
email = fields.Char(string='Email')
phone = fields.Char(string='Phone')
# Reference Field (Base)
reference = fields.Reference(
selection=[
('res.partner', 'Partner'),
('sale.order', 'Sales Order'),
('purchase.order', 'Purchase Order'),
],
string="Related Document"
)
# Another Reference Example
document_reference = fields.Reference(
selection=[
('res.partner', 'Partner'),
('res.users', 'User'),
('product.product', 'Product')
],
string="Document Reference"
)
# Dynamic Reference Fields
m2o_reference_model = fields.Char(
string='Reference Model',
help="Technical model name (example: sale.order)"
)
m2o_reference_id = fields.Many2oneReference(
string='Related Record',
model_field='m2o_reference_model'
)
status = fields.Selection(
[
('draft', 'Draft'),
('confirmed', 'Confirmed'),
('done', 'Done')
],
default='draft',
string="Status"
)
notes = fields.Text(string="Notes")
active = fields.Boolean(default=True)
View Definition
Create a file named personal_details_views.xml inside the views directory.
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- List View -->
<record id="view_personal_details_list" model="ir.ui.view">
<field name="name">personal.details.list</field>
<field name="model">personal.details</field>
<field name="arch" type="xml">
<list string="Personal Details">
<field name="name"/>
<field name="age"/>
<field name="gender"/>
<field name="email"/>
<field name="phone"/>
<field name="status"/>
</list>
</field>
</record>
<!-- Form View -->
<record id="view_personal_details_form" model="ir.ui.view">
<field name="name">personal.details.form</field>
<field name="model">personal.details</field>
<field name="arch" type="xml">
<form string="Personal Details">
<sheet>
<group>
<group>
<field name="name"/>
<field name="age"/>
<field name="gender"/>
<field name="status"/>
</group>
<group>
<field name="email"/>
<field name="phone"/>
<field name="active"/>
</group>
</group>
<notebook>
<!-- Reference Fields -->
<page string="Reference">
<group>
<field name="reference"/>
<field name="document_reference"/>
</group>
</page>
<!-- Dynamic Reference -->
<page string="Dynamic Reference">
<group>
<field name="m2o_reference_model"
placeholder="example: sale.order"/>
<field name="m2o_reference_id"/>
</group>
</page>
<!-- Notes -->
<page string="Notes">
<field name="notes"/>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<!-- Action -->
<record id="action_personal_details" model="ir.actions.act_window">
<field name="name">Personal Details</field>
<field name="res_model">personal.details</field>
<field name="view_mode">list,form</field>
</record>
<!-- Menu -->
<menuitem id="menu_personal_details_root"
name="Personal Details"
sequence="10"/>
<menuitem id="menu_personal_details"
name="Records"
parent="menu_personal_details_root"
action="action_personal_details"
sequence="10"/>
</odoo>
Fields Reference and Many2oneReference prove their usefulness when some level of flexibility is needed in data modeling.
The approach allows for not hardcoding fields and building more robust and reusable code that could adapt to any circumstances.
Use Reference relations when you know the set of models in advance.
Apply Many2oneReference relations where the model should be determined dynamically.
To read more about Complete Overview of Reference and Many2oneReference Fields in Odoo 19, refer to our blog Complete Overview of Reference and Many2oneReference Fields in Odoo 19.