Bạn có một One2many trong form — chẳng hạn "lịch sử điểm" của một thẻ thành viên. Mặc định, mỗi lần muốn thêm một dòng, Odoo bật một form con (dialog) lên, bạn điền, bấm Lưu, dialog đóng. Nhập ba dòng là ba lần mở–đóng. Với dữ liệu dạng bảng, kiểu nhập đó chậm phát bực. editable là thứ đổi hẳn trải nghiệm: cho gõ thẳng trên từng dòng của bảng, như điền một cái spreadsheet.

Một thuộc tính, đổi cả cách nhập

Chỉ cần thêm editable vào thẻ <list> bên trong One2many. Đây là đúng đoạn đang chạy trong module demo quan_ca_phe, cho field lich_su_ids ở tab "Lịch sử điểm":

Ảnh chụp đoạn mã XML nền tối, khối trên là bản trong module quan_ca_phe với field lich_su_ids chứa thẻ list editable bằng bottom và ba field ngay diem_cong ghi_chu; khối dưới minh hoạ cú pháp điều khiển theo cột gồm required bằng 1, readonly theo biểu thức trang_thai bằng huy, và column_invisible theo parent chấm an_ghi_chu

Hình 1: Khối trên là bản thật trong module — <list editable="bottom"> đủ để cả bảng con nhập được trực tiếp. Khối dưới là mẫu cú pháp Odoo 19 cho các thuộc tính điều khiển từng cột (không phải bản đang cài, chỉ minh hoạ). Chú ý column_invisible — của Odoo 19, thay cho kiểu attrs cũ.

editable nhận hai giá trị:

  • editable="top" — dòng thêm mới xuất hiện ở đầu bảng.
  • editable="bottom" — dòng mới nằm ở cuối bảng (hợp với lịch sử tăng dần theo thời gian, nên demo dùng cái này).

Không có editable, cùng cái <list> đó vẫn hiển thị danh sách, nhưng bấm vào dòng sẽ mở form con. Có editable, bấm vào ô là con trỏ nhảy vào đó luôn.

Nhìn nó chạy thật

Mở một thẻ thành viên (VIP-0001), sang tab "Lịch sử điểm", rồi bấm vào ô "Ghi chú" của một dòng:

Ảnh chụp giao diện Odoo thật, tab Lịch sử điểm với ba cột Ngày Điểm cộng Ghi chú; dòng đầu tiên đang được chọn và ô Ghi chú Mua 5 ly cà phê đang ở chế độ soạn thảo với chữ được bôi xanh; dưới cùng có dòng Thêm một dòng màu tím

Hình 2: Bấm vào ô "Ghi chú" của dòng đầu — cả dòng sáng lên (đang chọn) và ô chuyển thành ô nhập, gõ sửa được ngay tại chỗ. Không có dialog nào bật lên. Cuối bảng là link "Thêm một dòng": bấm vào để chèn một dòng trống rồi nhập tiếp — với editable="bottom" nó nằm ở cuối như thế này.

Cái hay là toàn bộ thao tác nằm trên cùng một màn hình: bấm ô này xong tab sang ô kia, Enter để xuống dòng dưới. Nhập một loạt dòng liền mạch, không lần nào rời form cha.

Điều khiển từng cột

Vì mỗi cột là một <field>, bạn đặt được thuộc tính lên từng ô — và trong Odoo 19 các biểu thức viết thẳng, không bọc trong attrs={...} như bản cũ:

<list editable="bottom">
  <field name="ngay" required="1"/>
  <field name="diem_cong" readonly="trang_thai == 'huy'"/>
  <field name="ghi_chu" column_invisible="parent.an_ghi_chu"/>
</list>
  • required="1" — ô bắt buộc nhập; để trống mà lưu là Odoo chặn ngay trên dòng.
  • readonly="<biểu thức>" — khoá ô theo điều kiện tính trên chính dòng đó.
  • column_invisible="<biểu thức>" — ẩn cả cột. Đây là điểm khác biệt quan trọng của list so với form: muốn ẩn nguyên cột thì dùng column_invisible, còn invisible chỉ ẩn ô trên một dòng. parent. cho phép đọc field của form cha để quyết định.

Biểu thức là Python thuần, chạy trên dữ liệu của dòng (và parent. cho form cha), nên bạn diễn đạt được những quy tắc kiểu "cột điểm khoá lại khi thẻ đã huỷ" mà không cần một dòng JavaScript nào.

onchange chạy ngay khi gõ

Điểm mạnh thật sự của editable list: onchange trên model của dòng chạy ngay lúc bạn sửa một ô, chưa cần lưu. Ví dụ, nếu model quan.lich.su.diem có một @api.onchange('diem_cong') để cảnh báo khi điểm cộng âm, thì vừa gõ số âm là cảnh báo hiện — ngay trên dòng đang nhập. Với form con thì bạn phải mở dialog mới thấy; với editable list, phản hồi tức thì tại chỗ.

Cùng cơ chế đó cho phép cột tính toán cập nhật sống: gõ số lượng, thành tiền tự nhảy — đây chính là cách các dòng chi tiết đơn hàng, hoá đơn của Odoo hoạt động.

Khi nào nên bật editable

  • One2many là dòng chi tiết (lịch sử, dòng đơn, bảng công) → gần như luôn nên editable. Người dùng nhập nhanh hơn hẳn.
  • One2many trỏ tới bản ghi nặng, nhiều field → giữ form con (dialog) lại hơn, vì nhồi hết vào một dòng bảng sẽ chật và khó đọc.
  • List độc lập ở cấp cao nhất (một action mở thẳng ra danh sách) cũng đặt editable được, cho sửa nhanh không cần vào form — nhưng cân nhắc, vì sửa nhầm trên bảng dễ hơn.

Ba ý mang về

  1. editable="top" / editable="bottom" biến <list> trong One2many thành nơi nhập trực tiếp — bấm ô là sửa, không mở form con; top/bottom quyết định dòng mới nằm ở đầu hay cuối.
  2. Điều khiển từng cột bằng thuộc tính viết thẳng (Odoo 19): required, readonly="<expr>", và đặc biệt column_invisible để ẩn nguyên cột (khác invisible chỉ ẩn một ô). parent. đọc được form cha.
  3. onchange của model dòng chạy ngay khi gõ, cho phản hồi và cột tính toán cập nhật tức thì — thứ khiến các dòng chi tiết đơn hàng/hoá đơn của Odoo mượt như vậy.

Editable list cho ta nhập gọn trên một bảng. Nhưng khi form phình to với hàng chục field, ta cần chia nó cho dễ thở. Phần sau nói về notebook và page — cách gom field thành các tab trong form.