odoo

Odoo Models Explained: Complete Guide to ORM and Custom Models

Odoo models (models.Model) have a variety of class attributes that define how the model behaves, how it’s stored, displayed, or interacted with in the system. Here’s a detailed explanation of all important class attributes, with examples and use cases.

1._name – Model Name (Mandatory for Custom Models)

  • Purpose: Defines the technical name of the model in the database.
  • Default models like res.partner already have _name.
  • Required for new custom models.

Example:

class MyEmployee(models.Model):
    _name = 'my.employee'
    name = fields.Char(string="Employee Name")
  • Table created in PostgreSQL: my_employee
  • Use _name to refer to this model in relations like many2one.

 

2._inherit – Inherit an Existing Model

  • Purpose: Extend or override an existing model.
  • Can be used with or without _name.

Example – Extend res.partner

class PartnerInherit(models.Model):
    _inherit = 'res.partner'

    employee_code = fields.Char(string="Employee Code")
  • Adds a new field to existing res.partner.
  • No new table is created.

Example – Inherit + Rename Table (Rare)

class NewPartner(models.Model):
    _name = 'new.partner'
    _inherit = 'res.partner'

Creates a new table but reuses fields from res.partner.

3._description – Model Description

  • Purpose: Human-readable description of the model
  • Appears in UI headers, documentation, and list views
class MyEmployee(models.Model):
    _name = 'my.employee'
    _description = "Employee Management System"

    name = fields.Char()

Optional but good practice for clarity.

 

4._rec_name – Record Display Name

Field used as display name in dropdowns, many2one, Kanban cards, etc.

rec_name is a special class attribute of a model that tells Odoo which field should be used as the "display name" for records

By default:

  • Odoo uses the field name as the rec_name if it exists.
  • If your model doesn’t have a name field, you must explicitly define rec_name, otherwise the record will appear as record id: 1.

Syntax:

class MyModel(models.Model):
    _name = 'my.model'
    _rec_name = 'custom_name_field'  # This is the important part

Example 1 – Default name field

from odoo import models, fields

class Employee(models.Model):
    _name = 'my.employee'
    
    name = fields.Char(string="Full Name")
    email = fields.Char(string="Email")
  • Here, rec_name is implicitly name
  • Anywhere you select an employee (many2one field), Odoo shows the value of name

Example 2 – Custom rec_name

from odoo import models, fields

class Employee(models.Model):
    _name = 'my.employee'
    _rec_name = 'email'  # Use email as display name

    first_name = fields.Char()
    last_name = fields.Char()
    email = fields.Char()
  • Now, in a dropdown or many2one field, records are displayed as email addresses instead of the default name (which doesn’t exist here).
  • Useful if your model doesn’t have a name or you want to show something else.

Example 3 – Using a computed field as rec_name

from odoo import models, fields, api

class Employee(models.Model):
    _name = 'my.employee'
    _rec_name = 'full_name'

    first_name = fields.Char()
    last_name = fields.Char()
    full_name = fields.Char(compute='_compute_full_name')

    @api.depends('first_name', 'last_name')
    def _compute_full_name(self):
        for rec in self:
            rec.full_name = f"{rec.first_name or ''} {rec.last_name or ''}".strip()
  • Here, full_name is a computed field
  • Records in a many2one field will show as "First Last"
  • Very common in HR, Contacts, and Employee models

Where rec_name is used

  1. Many2one fields

    employee_id = fields.Many2one('my.employee', string="Employee")

    Shows rec_name of selected employee

  2. Search views & filters
  3. Related widgets (Kanban, Form headers, etc.)
  4. Automatically generated references in Odoo logs

 

Advanced – Dynamic _rec_name with name_get

_rec_name only points to a single field, but if you want more dynamic display logic, override name_get.

class Employee(models.Model):
    _name = 'my.employee'

    first_name = fields.Char()
    last_name = fields.Char()
    department_id = fields.Many2one('hr.department')

    def name_get(self):
        result = []
        for rec in self:
            name = f"{rec.first_name} {rec.last_name}"
            if rec.department_id:
                name += f" ({rec.department_id.name})"
            result.append((rec.id, name))
        return result

Result:

  • Shows John Doe (IT Department) in many2one fields
  • Can include status, code, or any condition dynamically

 

5._order – Default Sorting

  • Purpose: Define default sorting order for records
  • Format: 'field_name asc|desc, field2 asc|desc'
class MyEmployee(models.Model):
    _name = 'my.employee'
    _order = 'joining_date desc, name asc'

    name = fields.Char()
    joining_date = fields.Date()
  • Records will be sorted by joining_date descending first, then name ascending.

 

6._table – Database Table Name

  • Purpose: Explicitly set the table name in the database.
  • By default, table name = _name with dots replaced by _.
class MyEmployee(models.Model):
    _name = 'my.employee'
    _table = 'company_employee'
  • Table in PostgreSQL: company_employee
  • Useful when migrating legacy databases.

 

7._auto – Automatic Table Creation

  • Purpose: Control if Odoo should create a database table for this model
  • Default: True
  • False ? Useful for abstract models, views, or transient models.
class MyAbstractModel(models.Model):
    _name = 'my.abstract'
    _auto = False  # No table is created

 

8._inherits – Delegation Inheritance

  • Purpose: Use fields from another model in a composition manner
  • Unlike _inherit (extension), _inherits creates a relation rather than modifying existing table
class MyEmployee(models.Model):
    _name = 'my.employee'
    _inherits = {'res.partner': 'partner_id'}

    partner_id = fields.Many2one('res.partner', required=True)
    employee_code = fields.Char()
  • The table stores only employee_code + partner_id
  • Accessing employee fields automatically delegates to res.partner.

 

9._transient – Transient / Wizard Models

  • Usually: TransientModel instead of Model
  • Automatically cleaned after a few hours
  • No permanent data storage.
class MyWizard(models.TransientModel):
    _name = 'my.wizard'
    _description = "Wizard Example"

    start_date = fields.Date()
    end_date = fields.Date()

 

10._log_access – Audit Fields

  • Default: True (creates create_uid, create_date, write_uid, write_date)
  • Set to False if you don’t want automatic tracking (rare)
class MyModel(models.Model):
    _name = 'my.model'
    _log_access = False

11._check_company – Multi-Company Validation

  • Default: False
  • If True, enforces company_id consistency on record creation and updates.
class MyEmployee(models.Model):
    _name = 'my.employee'
    _check_company = True

 


About author

author image

Amrit panta

Fullstack developer, content creator



Scroll to Top