Mastering Odoo 18 Command Class: The Modern Way to Handle Relations
By Braincuber Team
Published on February 3, 2026
If you've been developing in Odoo for a while, you've likely memorized the "magic tuples" like (0, 0, vals) or (6, 0, ids). While functional, these numerical codes are cryptic, prone to errors, and hard for new developers to understand.
Enter the Command class. Introduced in later versions and now the standard in Odoo 18, it provides a readable, Pythonic interface for managing One2Many and Many2Many relationships. This tutorial will guide you through replacing those old tuples with clean, expressive code.
line_ids = [(0, 0, {
'product_id': 10,
'qty': 5
})]
line_ids = [
Command.create({
'product_id': 10,
'qty': 5
})
]
What is the Command Class?
The Command class is a namespace that contains static methods corresponding to the 7 relational operations supported by Odoo. Instead of remembering that 0 means "create" and 2 means "delete", you simply call Command.create() or Command.delete().
from odoo import Command
# Now you can use Command.create(), Command.update(), etc.
The 6 Essential Commands
Let's look at real-world examples using a Sales Order context, where we manage order_line (One2many) and tag_ids (Many2many).
1. Create New Records
Replaces (0, 0, values). Creates a new record in the co-model and links it.
# Create a new sale order line
order.write({
'order_line': [
Command.create({
'product_id': product_id,
'product_uom_qty': 5,
'price_unit': 150.00
})
]
})
2. Update Existing Records
Replaces (1, id, values). Updates fields on an existing linked record.
# Update the price of line with ID 10
order.write({
'order_line': [
Command.update(10, {'price_unit': 200.00})
]
})
3. Delete Records Permanently
Replaces (2, id, 0). Removes the link AND deletes the record from the database. Use carefully!
# Delete the line completely
order.write({
'order_line': [
Command.delete(11)
]
})
4. Link Existing Records (Many2Many)
Replaces (4, id, 0). Adds a relationship to an existing record.
user.write({
'category_id': [
Command.link(14) # 14 is the ID of the tag
]
})
5. Unlink Records (Remove Relation)
Replaces (3, id, 0). Removes the link but keeps the record in the database. Common for Many2Many.
user.write({
'category_id': [
Command.unlink(14)
]
})
6. Clear All Records
Replaces (5, 0, 0). Removes all links from the field. For One2many, this deletes the records; for Many2many, it just unlinks them.
order.write({
'order_line': [
Command.clear()
]
})
7. Set Specific List (Replace All)
Replaces (6, 0, [ids]). Replaces the current list of linked IDs with a new one.
user.write({
'category_id': [
Command.set([1, 2, 3])
]
})
Readability: Command.create() is self-explanatory. (0,0,{}) is not.
Safety: No more accidental deletions because you typed 2 instead of 3.
Future-Proofing: It's the standard for Odoo 16, 17, and 18.
You can mix and match commands in a single list! For example, you can create a new record and delete an old one in the same .write() call: [Command.create({...}), Command.delete(12)].
Frequently Asked Questions
It is not strictly deprecated yet in Odoo 18, so existing code will still run. However, Odoo SA strongly recommends using the `Command` class for all new development and refactoring because it is much cleaner and less error-prone.
For Many2Many fields, `Command.delete(id)` (or tuple 2) behaves like `Command.unlink(id)` (tuple 3)—it typically just removes the relationship. However, standard Odoo practice suggests using `Command.unlink()` to be explicit that you are removing the link, not the record.
Yes! In `onchange` methods, you can return a dictionary of values where One2Many fields are updated using these commands. For example: `{'value': {'line_ids': [Command.clear(), Command.create({...})]}}`.
`Command.link(id)` adds a record to the **existing** list. `Command.set([ids])` **replaces** the entire list with the new IDs provided, essentially unlinking anything that isn't in the new list.
No. The command is just a data structure (a tuple wrapper). The record is only created when the command is processed by the Odoo ORM during a `.create()` or `.write()` method call.
