Đến giờ mọi view của ta là XML tĩnh — viết một lần, cố định. Nhưng đôi khi bạn cần view thay đổi theo dữ liệu lúc chạy: một bảng có một cột cho mỗi tháng trong năm hiện tại, một form thêm field theo cấu hình của công ty, một banner hiện số liệu thật. XML không làm được vì nó không biết dữ liệu. Giải pháp là ghi đè get_view — hàm Odoo gọi để lấy cấu trúc view — và sửa arch bằng code trước khi trả về client. Bài này chèn một banner tự đếm số thẻ vào form, ngay lúc mở.

Ghi đè get_view, sửa arch

get_view (Odoo 19; các bản trước tên là fields_view_get) trả về một dict có arch (chuỗi XML của view), model, models... Bạn gọi super(), rồi parse arch, sửa, và gắn lại:

from lxml import etree

@api.model
def get_view(self, view_id=None, view_type='form', **options):
    res = super().get_view(view_id=view_id, view_type=view_type, **options)
    if view_type == 'form':
        doc = etree.fromstring(res['arch'])       # parse arch thành cây
        sheet = doc.find('.//sheet')
        if sheet is not None:
            tong = self.search_count([])          # dữ liệu THẬT lúc chạy
            html = '<div class="alert alert-info">Toàn hệ thống có %d thẻ.</div>' % tong
            sheet.insert(0, etree.fromstring(html))  # CHÈN vào cây
            res['arch'] = etree.tostring(doc, encoding='unicode')
    return res

Ảnh chụp mã Python nền tối, import etree từ lxml, method get_view gắn api chấm model nhận view_id view_type và options gọi super chấm get_view lấy res, nếu view_type là form thì parse res arch bằng etree chấm fromstring thành doc, tìm thẻ sheet, nếu có thì đếm tong bằng search_count rồi tạo html một div alert-info chứa tổng số thẻ và insert vào đầu sheet, sau đó gán res arch bằng etree chấm tostring, cuối cùng trả res, kèm chú thích dùng khi cần sinh field cột nội dung động mà XML tĩnh không đủ và tránh lạm dụng vì get_view chạy mỗi lần mở view nên giữ nhẹ

Hình 1: Cơ chế. get_view là điểm can thiệp — Odoo gọi nó để lấy cấu trúc view gửi cho client. Ta gọi super() lấy arch gốc, dùng lxml.etree parse thành cây, chèn một phần tử (ở đây một <div class="alert"> tính từ search_count thật), rồi tostring gắn lại vào res['arch']. Client nhận arch đã sửa.

Xem thật: banner tự đếm

Cài override trên vào model thẻ, mở một form bất kỳ — banner xuất hiện với con số thật:

Ảnh chụp form Odoo thẻ VIP-0001, ngay dưới header và trên phần Mã thẻ có một dải banner màu xanh nhạt ghi Toàn hệ thống đang có 4 thẻ thành viên trong ngoặc banner này do get_view sinh động lúc chạy, bên dưới là nội dung form bình thường gồm Mã thẻ VIP-0001, cột Thông tin thẻ với Khách hàng Nguyễn Văn An và Ngày cấp, cột Điểm và số dư với Điểm tích luỹ 150 phần trăm Hạng thẻ Bạc Số dư 0 đô la

Hình 2: Banner động chạy thật. Dải xanh "Toàn hệ thống đang có 4 thẻ thành viên. (banner này do get_view sinh ĐỘNG lúc chạy)" được get_view chèn vào đầu <sheet> — con số 4 là search_count thật lúc mở form. Nếu thêm/xoá thẻ rồi mở lại, con số tự cập nhật. Đây là thứ XML tĩnh không làm được vì nó không biết dữ liệu.

Khi nào (và không nên) dùng

get_view mạnh nhưng nặng — dùng đúng chỗ:

  • Nên dùng khi cấu trúc view phụ thuộc dữ liệu/cấu hình lúc chạy mà XML không diễn đạt được: một cột cho mỗi kỳ (tháng/tuần) sinh theo dữ liệu, field theo Properties/thuộc tính động, ma trận nhập liệu theo danh mục.
  • KHÔNG nên khi có cách tĩnh: ẩn/hiện theo điều kiện → invisible; theo nhóm → groups; theo dữ liệu dòng → decoration. Những thứ này nhanh hơn nhiều và dễ bảo trì.
  • get_view chạy MỖI lần mở view — giữ nó nhẹ. Truy vấn nặng trong đó làm chậm mọi lần mở form. Cache/tối giản nếu buộc phải tính.

Vài lưu ý Odoo 19

  • Tên hàm là get_view trong Odoo 19; code cũ dùng fields_view_get (chữ ký và dạng trả về hơi khác) — khi nâng cấp phải viết lại.
  • Luôn gọi super() trước để có arch gốc (đã gộp mọi inherit); đừng dựng arch từ đầu.
  • Sửa res['arch'] là chuỗi — dùng lxml.etree để thao tác cây cho an toàn, rồi tostring(..., encoding='unicode').
  • Nếu thêm field mới vào arch động, field đó phải có trong model (và có thể phải bổ sung vào res để client biết định nghĩa) — chèn field không tồn tại sẽ lỗi.
  • Cân nhắc view_type: override nên chỉ tác động đúng loại view bạn nhắm (form/list...), tránh làm hỏng các loại khác.

Ba ý mang về

  1. Ghi đè get_view (Odoo 19; trước là fields_view_get) cho phép sửa cấu trúc view bằng code lúc chạy: gọi super(), parse res['arch'] bằng lxml.etree, chèn/sửa, tostring gắn lại. Đã chứng minh: banner "4 thẻ" tính từ search_count thật.
  2. Dùng khi view phụ thuộc dữ liệu/cấu hình lúc chạy (cột theo kỳ, field động) mà XML tĩnh không đủ; tránh lạm dụng — invisible/groups/decoration tĩnh nhanh và gọn hơn cho các nhu cầu thường.
  3. get_view chạy mỗi lần mở view → giữ nhẹ; luôn super() trước; field chèn động phải tồn tại trong model; nhắm đúng view_type.

Nhắc tới dữ liệu chảy vào view lúc chạy, có một kênh nhẹ hơn get_view rất nhiều mà ta đã gặp thoáng qua: Phần sau mổ xẻ context truyền từ action vào view — cách một action đặt sẵn giá trị mặc định, bật filter, hay đổi hành vi form qua context, không cần đụng tới cấu trúc view.