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_nameis 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_nameas a stored computed field and set_rec_name = 'complete_name'to avoid recomputation. - In
_name_search, always useself._search()instead ofself.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_namefield is empty or the model lacks anamefield. Set_rec_nameto a populated field. - "res.partner,1" instead of name:
display_nameis not being computed. Check for errors in_compute_display_name. - Search does not find records: override
_name_searchor set_rec_names_searchto include additional searchable fields.