Introduction
Most Odoo developers reach for the Many2one field when they need to link a record to another one. It works perfectly in the vast majority of cases. But sometimes you run into a situation where the linked document could be one of several different things: a sale order, a purchase order, a manufacturing order, or something else entirely depending on the context. That is exactly what the Reference field is built for.
The Reference field is one of the more flexible field types in the Odoo ORM. It lets you create a link to a record from any model in a predefined list, rather than always pointing to the same model. The user first selects the document type, then picks the specific record. The result is a polymorphic link that can adapt to different workflows.
This guide covers what the Reference field stores, how it behaves in the Odoo data model, how to create and customize it using Odoo Studio or Python, and practical business use cases where it genuinely adds value.
What is the Reference Field in Odoo
In the Odoo ORM, the Reference field is a special field type that stores a link to a record from any model you include in its selection list. This makes it fundamentally different from a Many2one field, which always points to one fixed model.
In the database, the Reference field stores its value as a plain text string in the format model_name,record_id. For example, a reference to sales order number 42 would be stored as sale.order,42. This is important to understand if you ever need to query or filter these records directly.
From the user interface perspective, a Reference field appears as a two-step input. The user first selects a document type from a dropdown list (for example: Sale Order, Invoice, or Project Task), and then picks the specific record from that model using a search field. The interface is clean and straightforward once users understand the two-step selection.
Here is what a basic Reference field definition looks like in Python:
from odoo import fields, models
class HelpDeskTicket(models.Model):
_inherit = 'helpdesk.ticket'
related_document = fields.Reference(
selection=[
('sale.order', 'Sale Order'),
('purchase.order', 'Purchase Order'),
('account.move', 'Invoice'),
('project.task', 'Project Task'),
],
string='Related Document',
)
The selection parameter is a list of tuples. Each tuple contains the technical model name (like sale.order) and the human-readable label that users will see in the dropdown. You control exactly which models are available in this list.
There is also a dynamic variant where the selection list is populated from the ir.model table at runtime, making all installed models available. This is useful for highly configurable tools but can become overwhelming for end users if not filtered properly.
In Odoo Studio, the Reference field is available under the field types panel and labeled Reference. When adding it through Studio, you can configure the list of selectable models directly from the interface without writing any code. This makes it one of the more accessible options among the advanced Odoo studio fields.
How the Field Works
Understanding how the Reference field stores and retrieves data is key to using it correctly in your Odoo development projects.
Storage in the Database
Unlike a Many2one field, which stores only an integer (the foreign key), the Reference field stores a string value that includes both the model name and the record ID. For example: sale.order,15. This is stored in a VARCHAR column in PostgreSQL. It means the database does not enforce any foreign key constraint on this column, which is a deliberate design choice to support the polymorphic nature of the field.
Because there is no database-level foreign key, Odoo does not automatically clean up Reference field values when the linked record is deleted. If a sale order is deleted, a Reference field pointing to it will still contain the old string value. This is one of the most important behavioral differences to keep in mind during Odoo development.
Accessing the Linked Record in Python
When you read a Reference field value in Python, Odoo returns the actual record object of the referenced model. You can access its fields directly, just like you would with a Many2one result. If the field is empty, it returns False.
ticket = self.env['helpdesk.ticket'].browse(1)
doc = ticket.related_document
if doc:
print(doc._name) # e.g. 'sale.order'
print(doc.name) # e.g. 'S00042'
print(doc.id) # e.g. 15
This is one of the convenient aspects of the Odoo ORM abstraction. Even though the underlying storage is a plain text string, the framework resolves it to a proper record object when you access it in code.
Key Field Attributes
These are the most relevant attributes you can configure on a Reference field in the Odoo framework:
- selection: A list of tuples defining which models are selectable. Can also be a method name (string) that returns such a list dynamically.
- string: The field label shown to users in the interface.
- required: Makes the field mandatory. The user must select both a model type and a record before saving.
- readonly: Prevents users from modifying the field value from the interface. Useful when the reference is set programmatically.
- help: A tooltip shown when the user hovers over the field label. Helpful for guiding users on what to select.
- compute: Like any other Odoo field type, a Reference field can be computed dynamically using a Python method. This is useful for automatically setting a reference based on business logic.
Filtering and Searching
Because the value is stored as a plain string, filtering on a Reference field requires specific domain syntax. To search for records linked to a specific document, you need to compose the string value yourself:
tickets = self.env['helpdesk.ticket'].search([
('related_document', '=', 'sale.order,15')
])
You can also filter by model type using a like operator:
tickets = self.env['helpdesk.ticket'].search([
('related_document', 'like', 'sale.order,')
])
Keep this string-based filtering behavior in mind when designing reports or computed fields that depend on Reference field values. It behaves differently from a typical Many2one domain filter.
Business Use Cases
The Reference field is most valuable in situations where the same kind of context link can point to documents of different types depending on the situation. Here are five practical examples from real business workflows.
1. Helpdesk Tickets Linked to Any Document
A support team handles tickets that can relate to very different things: an invoice dispute, a delivery problem, a contract question, or a defective product. Instead of creating a separate field for each document type, a single Reference field on the ticket model lets the agent link to whichever document is relevant. The agent picks the type first (Invoice, Sale Order, Delivery, etc.) and then picks the specific record. All context is in one field.
2. CRM Activities Linked to Multiple Source Documents
In a sales team, activities and follow-up tasks sometimes originate from different sources: a lead, a quote, an existing contract, or a support case. A Reference field on a custom activity model or CRM note lets salespeople tag the originating document without being locked into a single model. This is a natural fit for the Reference field in an Odoo CRM customization.
3. Notes and Annotations Across Modules
Some companies build a simple internal notes or annotation model to log observations across different business objects. A Reference field allows a single note record to be attached to a customer record, a project task, a manufacturing order, or a purchase order, all within the same field. This avoids duplicating the note model for every document type.
4. Document Approval Workflow
When implementing a generic approval workflow, the approval request needs to point to the document being approved. Since approvals can apply to purchase orders, expense reports, leave requests, or contracts, a Reference field on the approval model keeps the architecture clean. The same approval logic handles all document types without needing separate models for each.
5. Expense Reports Linked to Projects or Customer Orders
In some accounting setups, an expense needs to be linked to either a customer project or a sale order depending on the nature of the cost. A Reference field with both project.project and sale.order in its selection list gives the accountant the flexibility to attach the expense to whichever document applies. This is particularly useful in professional services and consulting firms running Odoo.
Creating or Customizing the Reference Field
There are two main ways to add a Reference field to an Odoo model: using Odoo Studio for a no-code approach, or writing it directly in Python for full control.
Using Odoo Studio
Odoo Studio makes it straightforward to add a Reference field to any form without touching code. Open Studio on the model you want to extend, go to the Fields panel, and add a new field of type Reference. You will be prompted to select which models should appear in the dropdown list. Studio stores the field as a manual field with a x_ prefix, just like other Odoo studio fields created through the interface.
This approach is ideal for quick customizations and for business analysts who want to extend forms without developer involvement. Keep in mind that Studio-created Reference fields may have limited support for advanced configurations like dynamic selection methods or computed values.
Technical Implementation in Python
For a full Odoo development approach, define the Reference field directly in a Python model. Here is a more complete example showing how to create a Reference field with a dynamic selection method:
from odoo import api, fields, models
class ApprovalRequest(models.Model):
_name = 'approval.request'
_description = 'Approval Request'
name = fields.Char(string='Request Name', required=True)
@api.model
def _get_document_types(self):
return [
('purchase.order', 'Purchase Order'),
('hr.expense.sheet', 'Expense Report'),
('hr.leave', 'Time Off Request'),
('sale.order', 'Sale Order'),
]
document_ref = fields.Reference(
selection='_get_document_types',
string='Document',
help='Select the document this approval relates to.',
)
Using a method for the selection parameter (by passing its name as a string) gives you more flexibility. You can add conditional logic, filter based on installed modules, or build the list dynamically from configuration records.
Creating via the XML-RPC API
You can also create a Reference field programmatically using the Odoo XML-RPC API. This is useful when deploying fields as part of a remote configuration notebook. The field type to use is reference and the selection is passed as a string representation of the list:
field_id = models.execute_kw(
ODOO_DB, uid, ODOO_API_KEY,
'ir.model.fields', 'create',
[{
'name': 'x_related_document',
'field_description': 'Related Document',
'model_id': model_id,
'ttype': 'reference',
'selection': "[('sale.order', 'Sale Order'), ('purchase.order', 'Purchase Order')]",
'state': 'manual',
}]
)
Note that when creating a Reference field through the API, the selection value is passed as a Python-evaluable string rather than an actual list. This is how Odoo stores and reads it from the ir.model.fields table.
Best Practices
Here are the most useful guidelines to follow when working with Reference fields in your Odoo data model.
- Keep the selection list short and meaningful. Do not include every model just because you can. Limit the list to the document types that genuinely make sense for the use case. A long list confuses users and makes the field harder to use correctly.
- Use Many2one when you always link to the same model. The Reference field is designed for polymorphic relationships. If the linked model never changes, a standard Many2one field is simpler, faster to query, and better supported by Odoo reporting tools.
- Always check for null values in computed fields. When a Reference field is empty, it returns
Falsein Python. Any code that accesses the linked record must check for this first to avoid errors. - Handle orphan references in automated actions. Since the database does not enforce referential integrity on Reference fields, it is a good idea to add an automated action or a scheduled action that periodically checks for and clears stale references pointing to deleted records.
- Use descriptive labels for each model in the selection. The second element of each tuple is what users see in the dropdown. Use clear, business-friendly labels rather than technical model names. Write "Customer Invoice" rather than "account.move".
- Document the field clearly in your technical specifications. Because Reference fields behave differently from standard Many2one fields, any developer who inherits your code will benefit from a clear explanation of why a Reference was chosen and which models it links to.
Common Pitfalls
These are the mistakes we see most often when developers or business analysts work with Reference fields for the first time.
Treating it Like a Many2one in Domain Filters
Developers sometimes write domain filters for a Reference field using the same syntax as a Many2one, for example [('document_ref', '=', 15)]. This will not work. The stored value is a string like sale.order,15, not just an integer. You must compose the full string value when building domain filters on a Reference field.
Forgetting That Deleted Records Leave Orphan Values
Because there is no foreign key constraint in the database, deleting a referenced record does not clear the Reference field value. If a sale order is deleted and a ticket still has sale.order,42 in its Reference field, reading that field will return False instead of raising an error. Code that relies on the reference being valid must always handle this case.
Overusing the Dynamic All-Models Selection
Odoo allows you to pass a method for the selection that returns all installed models from ir.model. While this is powerful, it is almost always too broad for a user-facing field. Presenting hundreds of models in a dropdown is confusing and leads to incorrect data entry. Always restrict the selection to a curated list of meaningful document types.
Expecting Native Group-by in Reports
Reference fields are stored as plain text strings in the database. They are not foreign keys, so standard Odoo group-by and pivot functionality does not work on them the way it does on Many2one fields. If you need to group or aggregate records by their linked document, you will need to handle this through custom code or a computed field that extracts the model name as a separate selection or char field.
Confusing Reference and Many2one in Odoo Studio
When using Odoo Studio, some users mix up the Reference field and the Many2one field because both let you link to another record. The key difference is that a Many2one always links to one specific model (you choose it at field creation time and it never changes), while a Reference field lets the user pick the model each time they fill in the field. If you created a Many2one when you needed a Reference, you will need to rebuild the field rather than trying to adapt the existing one.
Conclusion
The Reference field fills a gap that the Many2one field cannot cover on its own. When a link needs to be flexible enough to point to different document types depending on the situation, the Reference field is the right tool in the Odoo framework. It is straightforward to define, works with Odoo Studio for no-code setups, and integrates naturally into Python models for technical implementations.
The main things to keep in mind are the string-based storage format, the absence of automatic foreign key cleanup, and the need to filter using composed string values rather than plain IDs. Once you understand those differences, the field behaves predictably and reliably across your Odoo data model.
Whether you are building a generic approval workflow, linking support tickets to different document types, or designing a flexible note system that spans multiple modules, the Reference field gives you a clean and maintainable solution without needing to duplicate your logic for each model type.
Need Help with Your Odoo Implementation?
At Dasolo, we help companies implement, customize, and optimize Odoo to fit their actual business workflows. Whether you need to build custom field logic, design a data model from scratch, or extend an existing Odoo setup with new functionality, our team has the technical depth to get it done properly.
If you are working on an Odoo project and need guidance on field types, data architecture, or development best practices, feel free to reach out. We are happy to discuss your situation and help you find the right approach.