Một form tốt biết tự thay đổi theo dữ liệu: thẻ hạng Vàng thì hiện thêm ô ghi chú nội bộ; thẻ đủ điểm VIP thì bắt buộc gán khách hàng; thẻ đã khoá thì không cho sửa số dư. Trong Odoo, bạn làm những điều này không cần JavaScript — chỉ cần đặt một biểu thức điều kiện vào các thuộc tính invisible, readonly, required của field. Và Odoo 19 đã đổi hẳn cú pháp so với các bản cũ, gọn hơn nhiều. Bài này dựng thật và xem form đổi theo dữ liệu.
Cú pháp mới của Odoo 19: biểu thức thẳng vào thuộc tính
Ở Odoo cũ, bạn viết attrs="{'invisible': [('hang_the', '!=', 'vang')]}" — dài dòng và khó đọc. Odoo 19 bỏ hẳn attrs: đặt biểu thức Python trực tiếp vào thuộc tính:
<!-- REQUIRED động: bắt buộc nhập khách khi thẻ từ 100 điểm -->
<field name="partner_id" required="diem >= 100"/>
<!-- INVISIBLE động: ô ghi chú chỉ hiện với thẻ Vàng -->
<field name="ghi_chu_noi_bo" invisible="hang_the != 'vang'"/>
<!-- READONLY động: khoá số dư khi thẻ đã khoá -->
<field name="so_du" readonly="trang_thai == 'khoa'"/>

Hình 1: Cú pháp Odoo 19. Đặt biểu thức Python thẳng vào invisible/readonly/required. Biểu thức đánh giá trên field của bản ghi đang mở, dùng toán tử == != > < >= in not in and or not. Muốn ẩn/khoá cả một khối (group, page, div), đặt thuộc tính trên thẻ bao ngoài thay vì từng field.
Xem thật: cùng form, hai hạng thẻ khác nhau
Cùng một form, mở thẻ Vàng (150 điểm) và thẻ Bạc (85 điểm) cho ra hai giao diện khác nhau. Đây là thẻ Vàng:

Hình 2: Thẻ Vàng, 150 điểm. Hai điều kiện cùng kích hoạt: required="diem >= 100" làm nhãn "Khách hàng" đỏ và ô bắt buộc nhập (150 ≥ 100); invisible="hang_the != 'vang'" cho ô "Ghi chú nội bộ" hiện ra (vì là hạng Vàng). Form phản ứng theo đúng giá trị của bản ghi.
Giờ mở thẻ Bạc (85 điểm) — cùng form đó, khác hẳn:

Hình 3: Thẻ Bạc, 85 điểm, cùng form Hình 2. Khác biệt: nhãn "Khách hàng" màu đen bình thường (85 < 100 nên không bắt buộc), và ô "Ghi chú nội bộ" biến mất (không phải hạng Vàng). Không có JavaScript, không hai form riêng — chỉ một form với biểu thức điều kiện.
Ba thuộc tính, ba tác dụng
invisible="biểu_thức": ẩn field khi biểu thức đúng. Đặt trên thẻ bao (group,page,notebook,div) để ẩn cả khối. Lưu ý: field ẩn vẫn tồn tại trong dữ liệu — chỉ không hiện.readonly="biểu_thức": khoá không cho sửa khi đúng. Giá trị vẫn hiển thị, chỉ không nhập được.required="biểu_thức": bắt buộc nhập khi đúng — nhãn đỏ, không lưu được nếu để trống.
Biểu thức đọc field trên chính bản ghi đang mở, và Odoo tự cập nhật khi người dùng đổi giá trị: sửa diem từ 90 lên 100 ngay trên form, nhãn "Khách hàng" lập tức đỏ lên — không cần lưu.
Vài lưu ý Odoo 19
- Field dùng trong biểu thức phải có trong view. Nếu
invisibletham chiếuhang_thethìhang_thephải là một field trong form (có thể đểinvisible="1"nếu không muốn hiện nó). invisible="1"(hằng) ẩn vĩnh viễn — hay dùng để nạp field phục vụ biểu thức khác mà không hiện ra.- Kết hợp điều kiện bằng
and/or/notvà ngoặc:readonly="state == 'done' or is_locked". - Với list view, cùng cú pháp áp cho cột:
<field name="x" readonly="..."/>khoá ô theo dòng;column_invisible="..."ẩn cả cột. - Đây là thay đổi lớn khi nâng cấp từ Odoo cũ: mọi
attrs="{...}"phải viết lại thành thuộc tính trực tiếp.
Ba ý mang về
- Odoo 19 bỏ
attrs: đặt biểu thức Python thẳng vàoinvisible/readonly/requiredtrên field (hoặc thẻ bao để áp cả khối). Gọn và dễ đọc hơn cú pháp cũ. - Biểu thức đánh giá trên field của bản ghi đang mở và tự cập nhật khi người dùng nhập — đã chứng minh: cùng form, thẻ Vàng 150đ hiện ô ghi chú + bắt buộc khách (nhãn đỏ), thẻ Bạc 85đ ẩn ô ghi chú + không bắt buộc.
invisibleẩn (dữ liệu vẫn còn),readonlykhoá sửa,requiredbắt buộc nhập; field dùng trong biểu thức phải có mặt trong view (có thểinvisible="1").
Nói tới điều kiện hiển thị, còn một cách "tô màu theo điều kiện" nữa dành riêng cho list: Phần sau đi sâu decoration — tô màu và kiểu chữ cho từng dòng list theo biểu thức, để bảng dữ liệu tự làm nổi bật thứ cần chú ý.