Introduction
If you have ever opened a Sales Order in Odoo and noticed the list of order lines below the customer details, you have already seen the One2many field in action. It is one of the most important building blocks in the Odoo data model, and understanding how it works is essential for anyone involved in Odoo development or customization.
This guide explains what the One2many field is, how it works in the Odoo ORM, when to use it in your own setup, and how to create or configure it either through Odoo Studio or directly in Python code.
Whether you are a business user trying to understand how your data is structured, or a developer looking for a practical Odoo field tutorial, this article covers what you need to know.
What is the One2many Field in Odoo
The One2many field is a relational field type in the Odoo framework. It allows a single parent record to be linked to multiple child records from another model.
Think of it this way: one Customer can have many Invoices. One Sales Order can contain many Order Lines. One Project can hold many Tasks. The parent record holds a reference to all related child records through the One2many field.
In the Odoo interface, a One2many field typically appears as an embedded list directly inside a form view. This inline table lets users add, edit, or remove child records without ever leaving the parent form. It is one of the most visible and frequently used patterns in the Odoo user interface.
How It Differs from Other Relational Fields
Odoo has three main relational field types in its ORM:
- Many2one: A single record pointing to one record in another model (for example, a Sales Order pointing to one Customer).
- One2many: A single record linked to multiple records in another model (for example, a Sales Order linked to many Order Lines).
- Many2many: Multiple records on both sides linked to each other (for example, a Product linked to multiple Tags, and each Tag linked to multiple Products).
The One2many is specifically used when a child record belongs to one and only one parent. If a child record needs to be shared across multiple parents, Many2many is the right choice.
Among the Odoo field types, the One2many is unique because it does not store any data itself. It is a virtual field that reads data from the child model using a Many2one field on that child as its anchor point.
How the Field Works
Under the hood, the One2many field does not store anything on the parent model's database table. Instead, it relies on a Many2one field that exists on the child model and points back to the parent.
This is a core principle in the Odoo ORM: every One2many field has a corresponding Many2one field on the related model. The One2many is essentially a reverse lookup. Odoo finds all child records where the Many2one field equals the current parent record's ID.
The Relationship Between One2many and Many2one
When you define a One2many field in Python, you specify two key parameters:
- comodel_name: the model that holds the child records.
- inverse_name: the name of the Many2one field on the child model that points back to the parent.
Here is a simple example from a custom Odoo module, showing how a Service Contract links to multiple Deliverable records:
deliverable_ids = fields.One2many(
comodel_name='service.deliverable',
inverse_name='contract_id',
string='Deliverables'
)
In this example, contract_id is a Many2one field on the service.deliverable model pointing back to the contract. Without that Many2one existing first, the One2many cannot function.
What Happens in the Database
In the Odoo database, nothing is stored in the parent table for a One2many field. The data lives in the child model's table, where each child row has a foreign key column pointing to the parent record's ID.
When Odoo loads a parent record and needs to display its One2many field, it runs a SQL query equivalent to: find all rows in the child table where the foreign key matches the current parent ID. This is standard relational database behavior, and the Odoo ORM handles it automatically.
This design is also why the One2many field is considered part of the Odoo python fields that do not add columns to the database. It is purely a computed view of related records.
Business Use Cases
One2many fields appear throughout Odoo because they model real-world parent-child relationships naturally. Here are five practical examples drawn from common business workflows.
1. Sales Orders and Order Lines
The Sales module uses a One2many field called order_line_ids on the sale.order model to link to sale.order.line. Each line stores a product, quantity, unit price, and discount. Users can add or remove lines directly from the Sales Order form without navigating away.
2. Invoices and Invoice Lines
In the Accounting module, the account.move model uses a One2many field to manage invoice lines (account.move.line). Each line represents a product or service charged to the customer. The parent invoice aggregates the totals from all its child lines.
3. Projects and Tasks
The Project module uses a One2many field on project.project to display all tasks belonging to that project. In the form view, this can appear as a list, a kanban board, or a Gantt chart, all driven by the same underlying One2many relationship.
4. Contacts and Sub-contacts
The res.partner model has a child_ids One2many field that lists all individual contacts linked to a company. When you open a company record, the Contacts tab shows all employees or contact persons. Each of those sub-contacts has a Many2one field (parent_id) pointing back to the company.
5. Custom Models in Service or Manufacturing
When building a custom Odoo module, the One2many field is the natural choice for any scenario where one record owns a list of sub-records. Examples include: a maintenance request linked to multiple spare parts used, a training course linked to multiple session dates, or a service contract linked to a list of deliverables.
This flexibility is one of the reasons the One2many field is so central to Odoo customization and to building tailored data models for different industries.
Creating or Customizing the Field
There are two main ways to add a One2many field in Odoo: using Odoo Studio for a no-code approach, or writing Python code directly in a custom module.
Using Odoo Studio
Odoo Studio does not let you create a One2many field directly from the field picker on the parent model. This is because the One2many always requires a Many2one to already exist on the child model first.
The recommended process in Studio is:
- Open the child model in Studio and add a Many2one field pointing to the parent model.
- Once the Many2one is saved, go back to the parent model in Studio.
- Add a new field and choose the type "One2many". Studio will ask you to select the related model and the inverse Many2one field.
- The One2many field will then appear as an embedded list in your form view.
For many business users, this is the simplest path to creating One2many relationships without writing any code. Odoo Studio fields created this way behave exactly like their Python equivalents.
Using Python Code in a Custom Module
For developers working on Odoo development projects, defining One2many fields in Python gives full control over behavior, naming, and domain filters. Here is a complete example:
from odoo import fields, models
class ServiceContract(models.Model):
_name = 'service.contract'
_description = 'Service Contract'
name = fields.Char(string='Contract Name', required=True)
deliverable_ids = fields.One2many(
comodel_name='service.deliverable',
inverse_name='contract_id',
string='Deliverables'
)
class ServiceDeliverable(models.Model):
_name = 'service.deliverable'
_description = 'Service Deliverable'
contract_id = fields.Many2one(
comodel_name='service.contract',
string='Contract',
ondelete='cascade'
)
name = fields.Char(string='Description', required=True)
Notice that the Many2one field (contract_id) is defined on the child model first. The inverse_name in the One2many definition must match the Many2one field name exactly.
Creating the Field via the XML-RPC API
If you manage Odoo database fields programmatically, you can also create One2many fields through the XML-RPC API using the ir.model.fields model. The process follows the same rule: create the Many2one first, then define the One2many using the relation_field parameter pointing to the Many2one field name.
This approach is particularly useful when managing Odoo database fields across multiple environments or when automating Odoo customization workflows.
Best Practices
Based on experience implementing Odoo for clients across different industries, here are the key practices that make a difference when working with One2many fields.
- Always create the Many2one first. The One2many cannot exist without its inverse field. Whether you are using Studio, Python code, or the API, the Many2one on the child model must exist before you define the One2many on the parent.
- Use the
_idssuffix for field names. By convention in Odoo, One2many fields are named with the_idssuffix, for exampleline_ids,task_ids,deliverable_ids. This makes it immediately clear in the code that the field returns a recordset. - Set
ondelete='cascade'when appropriate. If child records should be automatically deleted when the parent is deleted, set this option on the Many2one field. This prevents orphan records accumulating in your database. - Keep the embedded list focused. Only show the most relevant columns in the One2many list view. Displaying too many columns makes the form slow and hard to read. Use the
optionalattribute for secondary columns that users can toggle on or off. - Use domain filters to restrict visible child records. You can add a
domainparameter to a One2many field definition to limit which child records are shown based on specific conditions. This is useful when a child model is shared across several parent models. - Handle large datasets carefully. If a parent record can have thousands of child records, avoid loading them all at once in the form view. Consider using a separate list view or adding pagination to the embedded table.
Common Pitfalls
Here are the most frequent mistakes that come up when working with One2many fields in Odoo, whether in development or in configuration.
Forgetting the Many2one Inverse
The most common error when creating fields in Odoo is defining a One2many without first creating the corresponding Many2one on the child model. Odoo will raise an error because the inverse field does not exist. Always check that the inverse_name you specify actually exists on the child model.
Using the Wrong Write Syntax
When updating a One2many field through Python code or the API, you need to use the Odoo command tuples syntax. You cannot assign a plain Python list to a One2many field. The correct way to use write commands looks like this:
(0, 0, values_dict)to create a new child record.(1, child_id, values_dict)to update an existing child record.(2, child_id, 0)to delete an existing child record.(4, child_id, 0)to add an existing child record to the relation.(5, 0, 0)to unlink all child records from the parent (without deleting them).
Trying to write a plain list like record.line_ids = [1, 2, 3] will not work as expected in the Odoo ORM.
Confusing One2many with Many2many
A One2many means each child record belongs to exactly one parent. If you find yourself needing a child record to be linked to multiple parents simultaneously, the right choice is a Many2many field, not a One2many. Using One2many in that scenario would force you to duplicate records, which creates data integrity problems.
Performance Problems with Large Sub-lists
If a One2many field displays hundreds or thousands of lines in a form view, page loading will slow down significantly. This is a common issue in Accounting (invoice lines) and Inventory (stock move lines). Use the limit attribute on the list view or redirect users to a separate list view for large datasets.
Orphan Records After Deletion
If you delete a parent record without setting ondelete='cascade' on the child's Many2one field, child records may remain in the database with a null or invalid parent reference. Over time this can clutter your database and cause unexpected behavior in views and reports. Always define a clear deletion policy when designing your data model.
Conclusion
The One2many field is a foundational piece of the Odoo ORM and the Odoo data model. It powers some of the most essential features in the platform, from Sales Order lines to Invoice lines to Project tasks. Once you understand how it works and how it relates to the Many2one field on the child model, a large part of Odoo's internal structure becomes much easier to read and extend.
Whether you are configuring Odoo for your business, customizing it through Studio, or building a custom module from scratch, the One2many field will be a tool you reach for regularly. Knowing when to use it, how to define it correctly, and how to avoid common mistakes will save you a lot of time and prevent data issues down the road.
If you are looking for more Odoo field tutorials and technical guides, explore the other articles in the Odoo Data & API collection.
Need Help with Your Odoo Implementation?
Dasolo helps companies implement, customize, and optimize Odoo to fit their specific business needs. Whether you need help designing your data model, building custom modules, creating fields in Odoo, or simply getting more value from your existing setup, our team is ready to assist.
If you have questions about your Odoo project or want to discuss how to best structure your data, reach out to us. We are happy to help.