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 đó.

display_name tên hiển thị của bản ghi trong Odoo 19 bỏ name_get: Odoo 17 trở về trước dùng def name_get self nhưng nay đã bỏ ở Odoo 19; Odoo 19 override _compute_display_name, decorator api.depends name hang_the partner_id tính lại khi các trường này đổi, def _compute_display_name self, hang bằng dict của selection hang_the, for the in self, ten bằng the.name hoặc Thẻ mới, nếu có hang_the thì ten bằng ten mở ngoặc tên hạng đóng ngoặc, nếu có partner_id thì ten bằng ten gạch tên partner, gán the.display_name bằng ten; display_name dùng ở đâu là nhãn trong ô Many2one khi chọn thẻ ở form khác, tiêu đề breadcrumb tên trong danh sách khi tham chiếu, kết quả tìm kiếm gợi ý

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:

Output _compute_display_name chạy thật trên module quan_ca_phe database blog19: Odoo 19 xác nhận name_get đã bị bỏ khỏi core, grep đếm def name_get trong odoo orm models.py ra 0, hasattr the name_get trả False; thẻ đầy đủ tên hạng khách, tạo the bằng T.create name TV-777 hang_the vang partner_id p.id, the.name trả TV-777, the.display_name trả TV-777 mở ngoặc Vàng đóng ngoặc gạch Cà Phê Việt Chi nhánh Hà Nội; thẻ tối giản chỉ tên và hạng mặc định bac, tạo the2 bằng T.create name TV-888, the2.display_name trả TV-888 mở ngoặc Bạc đóng ngoặc; display_name là trường computed nên đọc qua read được, the.read display_name trả TV-777 Vàng Cà Phê Việt Chi nhánh Hà Nội

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 ý

  1. 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_name và gán record.display_name.
  2. display_name là 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".
  3. display_name xuất hiện ở ô Many2one, breadcrumb, danh sách, tìm kiếm — giữ nhẹ; chỉ đổi trường nguồn thì dùng _rec_name thay 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 ý.