Introduction
Have you ever watched a user type a product code into a Many2one field, press Enter, and get 'No results found' - even though the exact record exists? That's the moment they start questioning your development skills.
By default, Odoo's Many2one fields search only on the model's _rec_name field (usually just name). When users need to search by product codes, phone numbers, email addresses, or composite fields, the default behavior fails.
So the real questions are:
- How does Odoo decide which field to display in a many2one dropdown?
- How does Odoo perform the search when users type in the field?
- How do we customize or extend this behavior safely?
The answer lies in several mechanisms:
- _rec_names_search
- _search_name() / _search_<field>()
- name_search()
Understanding these functions gives you full control over display names, search behavior, and user experience.
1. What is _rec_names_search in Odoo?
Introduced around Odoo 14, _rec_names_search is a class-level list that tells Odoo which fields to search when users type into a Many2one field.
class EstatePropertyType(models.Model):
_name = "estate.property.type"
_description = "Estate Property Type"
name = fields.Char(required=True)
code = fields.Char()
_rec_names_search = ['name', 'code'] # Search on both name AND code
Benefits of _rec_names_search
- Declarative, No method override needed, just a class attribute
- Performant, Odoo handles the domain construction optimally
- Automatic operator handling, Respects 'ilike', 'like', '=', etc.
- Future-proof, Follows Odoo's modern development patterns
2. What is _name_search and _search_name or _search_<field> in Odoo?
_search_name or _search_<field> is a search override method used to customize how a field is searched when using domain filters. It's part of Odoo's ORM field search mechanism. When it's called: When you filter/search records using a domain like [('name', 'ilike', 'foo')]
def _search_name(self, operator, value):
if operator not in ("ilike", "like") or not isinstance(value, str):
return [('name', operator, value)]
return [('name', operator, AccountTax._parse_name_search(value))]
_name_search is a name-based lookup method used specifically for the many2one widget autocomplete — when a user types in a many2one field to find related records. When it's called: When Odoo's UI (or name_search()) needs to find records matching a string, typically for dropdowns.
@api.model
def _name_search(self, name, domain=None, operator='ilike', limit=None, order=None):
if operator in ("ilike", "like"):
name = AccountTax._parse_name_search(name)
return super()._name_search(name, domain, operator, limit, order)
3. What is name_get in Odoo?
This function formats how records are displayed. name_get() does NOT affect search behavior. It only controls how a record is displayed after it's found.
def name_get(self):
res = []
for record in self:
name = record.name
if record.parent_id:
name = record.parent_id.name+' / '+name
res.append((record.id, name))
return res
Common Pitfall and Best Practice
❌ What NOT to Do
- Don't iterate all records in _name_search on models with thousands of records
- Don't forget to preserve existing args, these contain security rules and view filters
- Don't use _name_search when _rec_names_search suffices
✅ Best Practices
1. Always combine with existing args:
args = args or [] final_domain = expression.AND([args, custom_domain])
2. Use expression helpers for complex domains:
from odoo.osv import expression
domain = expression.OR([
[('field1', operator, name)],
[('field2', operator, name)]
])
3. Test with the 'Search More' dialog,it uses the same _name_search method.
4. Consider user expectations, If your users expect to search by code, description, and name, implement all three.
Conclusion
Making your Odoo models searchable isn't just about code, it's about user experience. When a salesperson can find a product by scanning a barcode, typing a name, or entering a code, you've saved them seconds that add up to hours.
Quick Decision Guide:
Start with _rec_names_search for 80% of cases .
Use _name_search() when you need conditional or cross-model logic Avoid unstored computed fields in search if performance matters.
