Skip to content

Understanding Models - The Foundation

What Is a Model?

Technical Definition

A Model is a table in the database that stores a specific type of information. Think of it like an Excel spreadsheet - each model is a different spreadsheet for different data.

Excel Analogy

Excel ConceptOdoo ConceptExample
Spreadsheet fileModelres.partner (Contacts)
Column headerFieldname, email, phone
RowRecordOne specific customer
CellField value"John Smith"

Model Naming Convention

Odoo models have a specific naming pattern: module.object or module.parent.child

Model NameWhat It StoresMenu Location
res.partnerCustomers, Vendors, ContactsContacts app
res.usersSystem Users (login accounts)Settings > Users
res.companyCompanies in the systemSettings > Companies
sale.orderSales Orders / QuotationsSales > Orders
sale.order.lineIndividual lines on a sales orderInside Sales Order form
purchase.orderPurchase OrdersPurchase > Orders
account.moveInvoices, Bills, Journal EntriesAccounting > Invoices
stock.pickingDelivery Orders, ReceiptsInventory > Transfers
product.productProduct VariantsProducts with variants
product.templateProduct TemplatesProduct form
hr.employeeEmployeesEmployees app
project.projectProjectsProject app
project.taskTasks within projectsProject > Tasks
crm.leadLeads and OpportunitiesCRM app

Naming Pattern Explained

Format: module.object or module.parent.child

  • res = "Resource" - core/base models used everywhere
  • sale = Sales module models
  • purchase = Purchase module models
  • account = Accounting module models
  • stock = Inventory module models
  • hr = Human Resources models
  • crm = Customer Relationship Management models

Types of Models

Odoo has three types of models. Each type stores data differently:

Technical NameWhat It MeansData StorageExamples
ModelRegular model - normal screensSaved permanently in databaseContacts, Sales Orders, Invoices, Products
TransientModelTemporary model - used for wizards (popup dialogs)Auto-deleted after 1 hour (by default)"Create Invoice" popup, "Register Payment" popup
AbstractModelTemplate model - provides features to other modelsNot stored (no database table)mail.thread (adds chatter to any model)

TransientModel = Wizards

A TransientModel (also called a "wizard") is a popup window that appears when you click action buttons. It collects input and performs an action.

"Transient" means temporary - the data you enter in these popups is automatically deleted after about an hour. This keeps the database clean.

Common wizards you use daily:

When You Click...This Wizard Appears
Sales Order → "Create Invoice" buttonPopup asking: Regular invoice or Down payment?
Invoice → "Register Payment" buttonPopup asking: Payment amount, journal, date
Transfer → "Validate" (with partial qty)Popup asking: Create backorder or not?
Settings → "Add a Language"Popup asking: Which language to install?

Important

If you close a wizard popup without clicking the action button, nothing is saved. That's normal - wizard data is meant to be temporary!

AbstractModel = Feature Packs

An AbstractModel is a reusable set of features that can be added to any model. It doesn't create its own database table - it just provides functionality that other models can use.

Common AbstractModels in Odoo:

AbstractModelWhat It AddsWhere You See It
mail.threadChatter (messages, followers, log)Bottom of Sales Orders, Invoices, Contacts
mail.activity.mixinScheduled Activities"Plan Activity" button on records
portal.mixinCustomer portal access"Share" or "Preview" button on quotations

How It Works: Chatter Example

mermaid
graph TD
    Mixin["mail.thread
    Feature Pack: Chatter"] --> SO[sale.order]
    Mixin --> INV[account.move]
    Mixin --> CRM[crm.lead]
    Mixin --> TASK[project.task]

    style Mixin fill:#6366f1,color:white

All these models have chatter at the bottom of their forms. Instead of coding chatter 4 times, Odoo coded it once in mail.thread and each model simply says "I want those features".

In Code: How Sale Order Gets Chatter

python
class SaleOrder(models.Model):
    _name = 'sale.order'
    _inherit = ['mail.thread', 'mail.activity.mixin']  # ← Gets chatter + activities!

    name = fields.Char(string='Order Reference')
    partner_id = fields.Many2one('res.partner')
    # ... other fields

The _inherit line adds all the chatter functionality. Without it, no chatter would appear on the form.

Key Points

  • If you see chatter on a form → that model inherits mail.thread
  • If a form has "Plan Activity" button → that model inherits mail.activity.mixin
  • Need chatter on a custom model? → Tell developers to add _inherit = ['mail.thread']
  • Each record has its own messages → The feature is shared, but data is separate per record

How to Find a Model Name

Method 1: Enable Developer Mode

  1. Go to Settings > General Settings
  2. Scroll down and click "Activate Developer Mode"
  3. Go to any form view
  4. Click the bug icon (Debug menu) > "View Metadata"
  5. You'll see the model name (e.g., sale.order)

Method 2: URL Inspection

Look at the URL in your browser. In Odoo 17+, URLs use path segments:

https://mycompany.odoo.com/odoo/sale.order/5

The model name appears in the URL path! (Older versions used hash format: #model=sale.order&id=5)

How Data is Actually Stored - The Relational Database

While the Excel analogy helps understand the basics, Odoo uses a relational database (PostgreSQL) which is more powerful. The key difference: tables are connected to each other using IDs.

When you create a Sales Order in Odoo, you're not just creating one record - you're creating multiple records across several tables, all linked together by their IDs.

What Happens When You Create a Sales Order?

Let's say you create a quotation for customer "Acme Corp" with 2 product lines. Here's what actually gets stored in the database:

1. The Contacts Table (res_partner)

idnameemailphone
42Acme Corpcontact@acme.com+1 555-0100

2. The Products Table (product_product)

idnamelist_pricedefault_code
15Laptop Pro1,200.00LAPTOP-001
23Wireless Mouse45.00MOUSE-001

3. The Sales Order Header (sale_order)

idnamepartner_id 🔗date_orderstateamount_total
108S0010842 → Acme Corp2024-12-09draft2,490.00

4. The Sales Order Lines (sale_order_line)

idorder_id 🔗product_id 🔗product_uom_qtyprice_unitprice_subtotal
201108 → S0010815 → Laptop Pro21,200.002,400.00
202108 → S0010823 → Mouse245.0090.00

Legend

🔗 = Foreign Key (ID linking to another table)

Understanding the Relationships

The "Header" RecordThe "Line" Records
sale_order (id: 108)sale_order_line (ids: 201, 202)
Stores order-level info: date, state, totalsEach product line = separate record
partner_id = 42 links to the customerorder_id = 108 links back to header
This is ONE record for the whole orderproduct_id links to the product

Why IDs Matter - The Magic of Relational Databases

Every record in Odoo has a unique id (also called primary key). This ID is:

  • Auto-generated: Odoo creates it automatically when you save
  • Unique: No two records in the same table have the same ID
  • Permanent: Once assigned, the ID never changes
  • The Link: Other tables reference this ID to create relationships

Example: When Odoo displays "Acme Corp" on the sales order, it's actually reading partner_id = 42, then looking up id 42 in the res_partner table to get the name.

The Full Picture: What Confirming a Sales Order Creates

When you click "Confirm" on a quotation, Odoo creates even more records:

mermaid
graph LR
    SO["sale.order
    S00108"] --> PICK["stock.picking
    Delivery Order"]
    SO --> INV["account.move
    Invoice"]
    PICK --> MOVE["stock.move
    Stock Movements"]
    INV --> AML["account.move.line
    Journal Items"]

    style SO fill:#8b5cf6,color:white
    style PICK fill:#3b82f6,color:white
    style MOVE fill:#10b981,color:white
    style INV fill:#ef4444,color:white
    style AML fill:#f59e0b,color:white

All these records are linked back to the original Sale Order using IDs!

Why This Matters for Consultants

  • Debugging: When something looks wrong, understanding that data lives in multiple tables helps you investigate
  • Reports: Custom reports often need to join multiple tables using these ID relationships
  • Data Migration: Moving data between systems requires understanding these connections
  • Smart Buttons: Those counts you see (like "2 Deliveries") are queries counting related records by ID

Excel vs Relational Database - Key Difference

ConceptExcel ApproachOdoo/Database Approach
Customer name on orderType "Acme Corp" directly in cellStore ID 42, look up name from res_partner
Customer changes nameMust update every order manuallyUpdate once in res_partner, all orders show new name
Order linesMultiple columns: Product1, Qty1, Product2, Qty2...Separate table with unlimited rows, linked by order_id
Find all orders for customerManually search/filterQuery: WHERE partner_id = 42

The Meta-Models (Registry)

ir.model (Model Registry)

ir.model is Odoo's meta-model - it stores information about all models in the system.

Location: Settings → Technical → Database Structure → Models

FieldTypeDescription
nameCharHuman-readable model name (e.g., "Sales Order")
modelCharTechnical model name (e.g., "sale.order"). Custom models start with "x_"
orderCharDefault SQL ordering (e.g., "date desc, id desc")
infoTextModel documentation/description
field_idOne2manyLink to all fields defined on this model (ir.model.fields)
access_idsOne2manyAccess control rules for this model (ir.model.access)
rule_idsOne2manyRecord rules / row-level security (ir.rule)
stateSelectionmanual = Created via Studio/UI, base = Defined in Python code
transientBooleanTrue for wizard models (auto-deleted after use)
abstractBooleanTrue for mixin models (no database table)
modulesChar (computed)Which installed modules define/extend this model
view_idsOne2many (computed)All views (form, tree, kanban, etc.) for this model
countInteger (computed)Total number of records in this model

ir.model.fields (Field Registry)

ir.model.fields stores metadata about every field in Odoo.

Location: Settings → Technical → Database Structure → Fields

This is where Studio stores custom field definitions.

FieldTypeDescription
nameCharTechnical field name (e.g., "partner_id"). Custom fields start with "x_"
field_descriptionCharHuman-readable label shown in UI (e.g., "Customer")
model_idMany2oneThe model this field belongs to (ir.model)
ttypeSelectionField type: char, integer, float, boolean, date, datetime, text, html, binary, selection, many2one, one2many, many2many, reference, monetary, properties
relationCharFor relational fields: target model name (e.g., "res.partner")
relation_fieldCharFor One2many: the Many2one field on the related model
requiredBooleanField must have a value (cannot be empty)
readonlyBooleanField cannot be edited by users
indexBooleanDatabase index for faster searches
storeBooleanTrue = saved in database, False = computed on-the-fly
copiedBooleanValue is copied when duplicating records
translateSelectionTranslation mode: standard, html_translate, xml_translate
company_dependentBooleanDifferent value per company (multi-company)
domainCharFilter for relational fields (e.g., "[('active','=',True)]")
selectionCharFor Selection fields: list of options
computeCharPython method name for computed fields
dependsCharFields that trigger recomputation

Auto-Generated Fields (Magic Columns)

Every Odoo model automatically gets these 5 fields:

FieldTypeDescription
idIntegerUnique identifier (primary key)
create_uidMany2oneUser who created the record
create_dateDatetimeWhen the record was created
write_uidMany2oneUser who last modified the record
write_dateDatetimeWhen the record was last modified

Quick Tips

  • 💡 Every model gets 5 magic columns: id, create_uid, create_date, write_uid, write_date
  • 💡 Custom fields must start with x_ (or x_studio_ if created via Studio)
  • 💡 _rec_name controls what shows in dropdowns (default is name)
  • 💡 The active field (if present) controls the "Archived" filter

Practical Use Cases

Finding Which Module a Model Belongs To

  1. Enable Developer Mode
  2. Go to Settings → Technical → Database Structure → Models
  3. Search for the model name
  4. Look at the "Modules" field to see which module(s) define it

Finding All Fields on a Model

  1. Enable Developer Mode
  2. Go to Settings → Technical → Database Structure → Fields
  3. Filter by Model = your model name
  4. You'll see all fields including custom ones

Checking If a Model Has Chatter

  1. Look at the form - is there a message log at the bottom?
  2. Or: Go to ir.model, search for the model, check if it inherits mail.thread

Common Mistakes

Common Mistakes to Avoid

  • Custom field name rejected: You forgot the x_ prefix
  • Dropdown shows "42" instead of name: The model has no name field and _rec_name isn't set
  • Wizard data lost: You closed the wizard without clicking a button (TransientModels are temporary)
  • Can't find a record: The record might be archived - check the "Active" filter
  • "Unknown model" error: The module defining the model isn't installed

Interactive Quiz

Test your understanding of Odoo models with these interactive questions:

What's the difference between Model and TransientModel?
Which columns exist automatically on every Odoo model?
What prefix must custom fields have in Odoo?
What does the mail.thread AbstractModel add to a model?