Skip to content

Odoo display_name and _compute_display_name: Complete Reference

DeployMonkey Team · March 23, 2026 11 min read

How Odoo Displays Record Names

Every Odoo record has a display_name that appears in Many2one dropdowns, breadcrumbs, chatter messages, and log entries. By default, Odoo uses the name field. Customizing how records are named is essential for usability.

The _rec_name Attribute

The _rec_name attribute specifies which field to use as the record name. By default it is 'name':

class ProductCategory(models.Model):
    _name = 'product.category'
    _rec_name = 'complete_name'  # Use complete_name instead of name

    name = fields.Char(required=True)
    complete_name = fields.Char(
        compute='_compute_complete_name', store=True)

If your model does not have a name field, you must set _rec_name to an existing field, or Odoo will fall back to displaying the record ID.

display_name Computed Field

The display_name field is automatically present on every model. It is computed by _compute_display_name() and is not stored. By default, it returns the value of the _rec_name field.

# Default behavior (no override needed):
# display_name = record.name (or record._rec_name field)

partner = self.env['res.partner'].browse(1)
print(partner.display_name)  # "Acme Corp"

Customizing _compute_display_name

Override _compute_display_name() to create rich display names combining multiple fields:

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

    name = fields.Char(required=True)
    job_title = fields.Char()
    department_id = fields.Many2one('hr.department')

    def _compute_display_name(self):
        for employee in self:
            parts = [employee.name]
            if employee.job_title:
                parts.append(f'({employee.job_title})')
            employee.display_name = ' '.join(parts)

Now Many2one dropdowns and breadcrumbs show "John Smith (Software Engineer)" instead of just "John Smith".

Context-Dependent Display Names

The display name can change based on context:

class ProductProduct(models.Model):
    _inherit = 'product.product'

    def _compute_display_name(self):
        for product in self:
            name = product.name
            if self.env.context.get('display_default_code') \
                    and product.default_code:
                name = f'[{product.default_code}] {name}'
            if self.env.context.get('show_price') \
                    and product.list_price:
                name = f'{name} ({product.list_price:.2f})'
            product.display_name = name

Then in views:

<field name="product_id"
       context="{'display_default_code': True}"/>
<!-- Dropdown shows: [SKU001] Widget Pro -->

Hierarchical Display Names

For tree-structured models (categories, locations), show the full path:

class Category(models.Model):
    _name = 'product.category'
    _parent_name = 'parent_id'

    name = fields.Char(required=True)
    parent_id = fields.Many2one('product.category')
    complete_name = fields.Char(
        compute='_compute_complete_name', store=True)

    @api.depends('name', 'parent_id.complete_name')
    def _compute_complete_name(self):
        for category in self:
            if category.parent_id:
                category.complete_name = \
                    f'{category.parent_id.complete_name} / {category.name}'
            else:
                category.complete_name = category.name

    def _compute_display_name(self):
        for category in self:
            category.display_name = category.complete_name or category.name

Result: "All / Furniture / Chairs" instead of just "Chairs".

name_search and Autocomplete

When users type in a Many2one field, Odoo calls _name_search() to find matching records. By default it searches the _rec_name field with ilike:

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

    @api.model
    def _name_search(self, name='', domain=None,
                     operator='ilike', limit=100, order=None):
        domain = domain or []
        if name:
            # Search by name, email, phone, or reference
            domain = ['|', '|', '|',
                ('name', operator, name),
                ('email', operator, name),
                ('phone', operator, name),
                ('ref', operator, name),
            ] + domain
        return self._search(domain, limit=limit, order=order)

This lets users find partners by typing their email or phone number, not just their name.

The _rec_names_search Attribute

Since Odoo 16, you can declare additional searchable fields without overriding _name_search:

class Product(models.Model):
    _name = 'product.product'
    _rec_names_search = ['name', 'default_code', 'barcode']

    name = fields.Char()
    default_code = fields.Char()  # internal reference
    barcode = fields.Char()

Odoo automatically searches all listed fields when users type in Many2one dropdowns.

display_name in List Views

In list views, display_name is used for Many2one columns:

<list>
  <!-- Shows display_name of the partner -->
  <field name="partner_id"/>

  <!-- Shows display_name of the record itself -->
  <field name="display_name"/>
</list>

Performance Considerations

  • display_name is not stored by default. Every time it is accessed, _compute_display_name() runs. For list views showing many records, this can be slow if the computation is complex.
  • Use complete_name as a stored computed field and set _rec_name = 'complete_name' to avoid recomputation.
  • In _name_search, always use self._search() instead of self.search() for better performance (returns IDs, not recordsets).

Common Patterns

Code + Name

def _compute_display_name(self):
    for record in self:
        if record.code:
            record.display_name = f'[{record.code}] {record.name}'
        else:
            record.display_name = record.name

Address Format

def _compute_display_name(self):
    for record in self:
        parts = [record.name]
        if record.city:
            parts.append(f'({record.city})')
        record.display_name = ' '.join(parts)

Date-Qualified Name

def _compute_display_name(self):
    for record in self:
        if record.date_start:
            date_str = record.date_start.strftime('%Y-%m')
            record.display_name = f'{record.name} [{date_str}]'
        else:
            record.display_name = record.name

Troubleshooting

  • Blank display names: the _rec_name field is empty or the model lacks a name field. Set _rec_name to a populated field.
  • "res.partner,1" instead of name: display_name is not being computed. Check for errors in _compute_display_name.
  • Search does not find records: override _name_search or set _rec_names_search to include additional searchable fields.