Skip to content

Relationships - Many2one, One2many, Many2many

Why Relationships Are Critical

Relationships connect data across different models. Understanding them is important for:

  • Understanding how data flows in Odoo
  • Creating proper filters and reports
  • Designing new features correctly

The Three Relationship Types

mermaid
graph TD
    M2O["Many2one
    N : 1"] -->|Many orders belong to One customer| C[Customer]
    O2M["One2many
    1 : N"] -->|One customer has Many orders| O[Orders]
    M2M["Many2many
    N : N"] ---|Many products in Many categories| CAT[Categories]
TypeCardinalityUI DisplayDatabase Storage
Many2oneMany → OneDropdownForeign key ID in column
One2manyOne → ManyEmbedded listNot stored (virtual)
Many2manyMany ↔ ManyTags/multi-selectJunction table

What Is Many2one?

A Many2one field creates a link from many records in one model to one record in another model.

  • UI: Dropdown / Search field
  • Storage: Just an ID number (foreign key)
  • Example: Many Sale Orders → One Customer

Real-World Example: Sales Orders

mermaid
graph LR
    S1[Sale Order #1] --> C[Customer: Acme Corp]
    S2[Sale Order #2] --> C
    S3[Sale Order #3] --> C
    style C fill:#10b981,color:white

Many Sale Orders can belong to One Customer.

Common Many2one Fields

FieldOn ModelLinks ToBusiness Meaning
partner_idsale.orderres.partnerWhich customer is this order for?
product_idsale.order.lineproduct.productWhich product is on this line?
company_id(many models)res.companyWhich company owns this record?
user_idsale.orderres.usersWho is the salesperson?
department_idhr.employeehr.departmentWhich department?
project_idproject.taskproject.projectWhich project does this task belong to?

Many2one Field Attributes

AttributePurposeExample
comodel_nameTarget model'res.partner'
ondeleteWhat happens when target is deleted'restrict', 'cascade', 'set null'
domainFilter available options[('is_company', '=', True)]
contextPass context to linked views{'default_type': 'out_invoice'}

One2many - The Reverse View

What Is One2many?

A One2many field shows all records that link back to the current record via a Many2one.

  • Important: One2many fields are NOT stored in the database. They are computed by looking at the reverse relationship.
  • UI: Embedded list/table (e.g., order lines within an order)

Real-World Example: Customer's Orders

On the Customer form, you see a list of all their orders:

mermaid
graph TD
    C[Customer: Acme Corp] -->|order_ids| List
    subgraph List [One2many List]
        O1[Sale Order #1]
        O2[Sale Order #2]
        O3[Sale Order #3]
    end
    style C fill:#10b981,color:white

The customer doesn't "store" these orders - Odoo looks up "all orders where partner_id = this customer"

One2many Field Attributes

AttributePurposeExample
comodel_nameTarget model (the child)'sale.order.line'
inverse_nameMany2one field on child pointing back'order_id'
domainFilter which records to show[('state', '=', 'active')]
copyCopy lines when duplicating parentTrue or False

One2many Limitations

Because One2many is not stored in the database, you cannot:

  • Use it in "Group By" reports
  • Filter by it directly in domains (some limitations)
  • Export it directly to Excel (export from child model instead)

What Is Many2many?

A Many2many field allows linking multiple records on both sides.

  • Storage: Creates a "junction table" in the database
  • UI: Tags or multi-select dropdown

Real-World Example

ProductsCategories

ProductCategories
LaptopElectronics, Computers, Office Equipment
KeyboardElectronics, Office Supplies, Computer Accessories
MouseElectronics, Computer Accessories

The same product can be in multiple categories, and the same category can contain multiple products.

Common Many2many Fields

FieldOn ModelLinks ToUsage
tag_idssale.ordercrm.tagCategorizing orders
category_idres.partnerres.partner.categoryCustomer tags
tax_idssale.order.lineaccount.taxMultiple taxes on one line
user_idsres.groupsres.usersUsers in a security group

Many2many Field Attributes

AttributePurposeExample
comodel_nameTarget model'res.partner.category'
relationJunction table name'partner_category_rel'
column1Column for current model ID'partner_id'
column2Column for target model ID'category_id'

Quick Reference: Relationship Summary

TypeStored?UI DisplayCan Group By?Example
Many2oneYes (ID)DropdownYesOrder → Customer
One2manyNoEmbedded ListNoCustomer → Orders
Many2manyYes (Junction)TagsLimitedProduct ↔ Categories

Deletion Behavior (ondelete)

When you delete a record (e.g., a Customer), what happens to the linked records (Orders)?

BehaviorEffectExample
RestrictBlocks deletion (Error message)Can't delete Customer if they have Invoices
Set NullClears the link (Field becomes empty)Employee leaves; tasks stay but user_id cleared
CascadeDeletes linked recordsDelete an Order → All Order Lines deleted

"Cannot Delete" Errors

If you get a "Reference Error" preventing deletion, it's usually ondelete='restrict'. You must first archive, reassign, or delete the related records.

Real-World Analogy

BehaviorAnalogy
RestrictCan't close a bank if customers have money there
Set NullEmployee leaves company - their records stay but employer field is cleared
CascadeDelete a department - all positions in it are also eliminated

Definition

A related field displays information from a linked record directly on the current form. It "reaches through" a Many2one to show data. Example: Showing the Customer's Country on the Sales Order.

Visible OnShows FieldSource Path
Sales OrderCustomer's delivery addresspartner_shipping_id.street
Invoice LineInvoice Status (Draft/Posted)move_id.state
Stock MoveCustomer on the transferpicking_id.partner_id
EmployeeManager Namedepartment_id.manager_id.name
TypeBehaviorUse Case
Not Stored (Default)Always live data, can't group byDisplay-only info
StoredCopies value, can group by, might get "stale"Reporting, filtering

Stale Data Risk

Stored related fields copy the value at the time it's computed. If the source changes without triggering recomputation, the related field may show outdated data.

Relationship Decision Guide

Business NeedRelationship TypeExample
Link to one other record (dropdown selection)Many2oneOrder → Customer
Show all linked items as a listOne2manyCustomer form showing all their orders
Tag or categorize with multiple optionsMany2manyProduct ↔ Multiple categories
Display info from linked record (read-only)Related fieldShow customer's country on order

Common Business Patterns

Pattern 1: Document with Lines

Almost every business document in Odoo follows this structure:

Header (Parent)Lines (Children)
Sales OrderSales Order Lines
Purchase OrderPurchase Order Lines
Invoice (account.move)Invoice Lines (account.move.line)
Delivery Order (stock.picking)Stock Moves (stock.move)

The header "owns" the lines. Delete an order → lines are deleted too (cascade).

Pattern 2: Hierarchies (Self-References)

Some models have parent-child relationships within themselves:

ModelHierarchy Example
Product CategoriesElectronics > Computers > Laptops
DepartmentsSales > Europe > France
Chart of AccountsAssets > Current Assets > Bank
LocationsWarehouse > Shelf A > Bin 1

These use a parent_id Many2one field pointing to the same model.

Pattern 3: Tagging / Categorization

When something can belong to multiple categories simultaneously:

ModelTag FieldUsage
CRM Leadstag_idsHot Lead, VIP, Trade Show
Productsproduct_tag_idsMultiple website categories
Contactscategory_idSupplier, Reseller, Partner

These use Many2many relationships displayed as colored tags in the UI.

Product Template vs Product Variant

Confusing Concepts - Must Understand!

product.template and product.product are two different models!

ModelDefinitionExample
product.templateThe Master (Shared info)"T-Shirt" with name, description, category
product.productThe Variant (Specific item)"T-Shirt, Red, Size M" with unique barcode

Key insight: Sales orders use product.product (variant), but the product form edits product.template.

Similarly: Users and Partners

res.users (login accounts) "inherits" from res.partner (contacts). Every user IS a contact with additional login capabilities:

  • Creating a user automatically creates a contact
  • User's name, email, phone come from their partner record
  • You can message a user through the contact system

Why Can't I Group By Some Fields?

One2many Fields Are Virtual

One2many fields don't actually exist in the database - they're calculated on-the-fly. This means:

  • Cannot Group By: "Group orders by order lines" doesn't make sense
  • Cannot Filter Directly: Use the child model instead
  • Cannot Export: Export from the line model, not the parent

Workaround: Filter Through Child Model

Instead of trying to filter orders by product on the order line:

  • Wrong approach: Search orders where order_line contains product X
  • Right approach: Search order lines for product X, then look at their orders

In the UI, use the order line list view and group by Order to see the same data.

Knowledge Check

Q1: Sales Order to Order Lines - what relationship?

Answer: One2many on Order, Many2one on Line

The line stores the order_id (Many2one), and the order has a One2many pointing back.

Q2: Which relationship creates an actual column in the database?

Answer: Many2one

Many2one stores the foreign key ID in the table. One2many is virtual. Many2many uses a separate junction table.

Q3: Products and Tags - what relationship?

Answer: Many2many

Multiple products can have multiple tags. Odoo creates a junction table to store the connections.

Q4: What happens when you delete a customer with orders if ondelete='restrict'?

Answer: Deletion is blocked with an error message

Restrict prevents deletion and shows an error. You must first delete/archive the orders.

Q5: Why can't you Group By a One2many field?

Answer: One2many fields are not stored in the database

They're computed on-the-fly by looking at the reverse Many2one relationship. Since there's no column, there's nothing to group by.

Q6: What's the difference between product.template and product.product?

Answer: Template is the master, Product is the variant

product.template stores shared info (name, description). product.product stores variant-specific info (barcode, specific attributes). Sales orders use the variant.