Đế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

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:

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_viewchạ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_viewtrong Odoo 19; code cũ dùngfields_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ùnglxml.etreeđể thao tác cây cho an toàn, rồitostring(..., 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ề
- 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ọisuper(), parseres['arch']bằnglxml.etree, chèn/sửa,tostringgắn lại. Đã chứng minh: banner "4 thẻ" tính từsearch_countthật. - 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/decorationtĩnh nhanh và gọn hơn cho các nhu cầu thường. get_viewchạy mỗi lần mở view → giữ nhẹ; luônsuper()trước; field chèn động phải tồn tại trong model; nhắm đúngview_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.