Khi chọn một "Thẻ thành viên" trong ô Many2one ở form khác, Odoo hiện chuỗi gì? Mặc định là trường name. Nhưng bạn thường muốn giàu thông tin hơn: "TV-777 (Vàng) - Cà Phê Việt". Chuỗi đó là display_name. Và đây là bài thứ hai trong loạt có phát hiện thay đổi API: Odoo 19 đã bỏ name_get — cách cũ để tùy biến tên hiển thị.
Phát hiện thật: name_get đã biến mất
Trong Odoo ≤17, bạn override def name_get(self) để đổi tên hiển thị. Tôi grep core Odoo 19 và không tìm thấy nó nữa:
docker exec odoo19 grep -c 'def name_get' odoo/orm/models.py
# 0
Trên bản ghi thật, hasattr(rec, 'name_get') cũng trả False. Thay vào đó, display_name là một trường computed, tính bằng _compute_display_name — bạn override method đó.

Hình 1: Odoo 19 thay name_get bằng _compute_display_name — override nó và gán record.display_name. Vì display_name là computed, nhớ khai @api.depends các trường nó phụ thuộc (ở đây name, hang_the, partner_id) để nó tự cập nhật khi chúng đổi.
Cho nó chạy thật
Tôi thêm _compute_display_name tùy biến vào module quan_ca_phe, nâng cấp, rồi kiểm. Output thật trên db blog19:

Hình 2: Output thật. name_get đã biến mất (grep = 0, hasattr = False). Thẻ TV-777 có display_name = "TV-777 (Vàng) - Cà Phê Việt - Chi nhánh Hà Nội" — đúng công thức tôi viết. Thẻ tối giản TV-888 ra "TV-888 (Bạc)" (hạng mặc định). Vì là trường computed, đọc qua read(['display_name']) cũng ra đúng chuỗi.
display_name dùng ở đâu?
display_name xuất hiện khắp giao diện:
- Ô Many2one: khi bản ghi này được tham chiếu từ form khác, ô hiện
display_name. - Breadcrumb: tiêu đề đường dẫn khi mở form.
- Danh sách/kanban khi bản ghi hiển thị qua tham chiếu.
- Kết quả tìm kiếm gợi ý trong ô Many2one.
Tùy biến display_name giàu thông tin giúp người dùng phân biệt các bản ghi na ná nhau — ví dụ nhiều thẻ cùng tiền tố thì thêm hạng/khách vào cho dễ nhận.
Ba lưu ý khi override
Luôn khai @api.depends
display_name là computed — thiếu @api.depends thì nó không tự cập nhật khi trường nguồn đổi (bug âm thầm, như đã nói ở bài computed).
Luôn for rec in self
Method chạy trên recordset — phải duyệt và gán cho từng bản ghi, giống mọi hàm compute.
Đừng làm nặng
display_name được đọc rất nhiều (mỗi ô Many2one, mỗi danh sách). Đừng truy vấn nặng hay tính toán phức tạp trong đó — giữ nó nhẹ, chỉ ghép chuỗi từ các trường sẵn có.
_rec_name — cách nhẹ hơn
Nếu chỉ muốn display_name lấy từ một trường khác (không phải name), không cần override gì — chỉ đặt _rec_name:
class Model(models.Model):
_rec_name = 'ma_so' # display_name mặc định lấy từ 'ma_so' thay vì 'name'
Đây là cách nhẹ nhất; chỉ khi cần ghép nhiều trường mới override _compute_display_name. _rec_name là chủ đề bài sau.
Nhớ ba ý
- Odoo 19 bỏ
name_get(grep core = 0,hasattr= False); tùy biến tên hiển thị giờ bằng override_compute_display_namevà gánrecord.display_name. display_namelà trường computed — nhớ@api.depends+for rec in self; đo thật: ra "TV-777 (Vàng) - Cà Phê Việt - Chi nhánh Hà Nội".display_namexuất hiện ở ô Many2one, breadcrumb, danh sách, tìm kiếm — giữ nhẹ; chỉ đổi trường nguồn thì dùng_rec_namethay vì override.
Người dùng thấy display_name rồi, nhưng khi họ gõ tìm trong ô Many2one, Odoo khớp theo trường nào? Phần sau đi vào _rec_name và name_search — chọn trường định danh và tùy biến cách tìm kiếm gợi ý.