Nhìn các dòng chi tiết đơn hàng, các bước trong quy trình, hay danh sách công việc trong Odoo — nhiều chỗ cho bạn giữ một cái tay nắm ở đầu dòng rồi kéo lên/xuống để sắp lại thứ tự. Trải nghiệm đó trông có vẻ cầu kỳ, nhưng cơ chế thì tối giản: một field số tên sequence, một dòng _order, và widget handle. Bài này gắn tay nắm kéo–thả đó vào danh sách "lịch sử điểm" của module demo, rồi kéo thật để xem thứ tự đổi.

Đúng hai thứ ở model

Widget handle không tự lưu thứ tự vào đâu cả — nó chỉ là cái tay nắm. Thứ tự thật nằm ở một field số nguyên mà quy ước đặt tên là sequence, và model phải sắp xếp theo nó qua _order:

Ảnh chụp đoạn mã nền tối hai phần: phần Python khai class LichSuDiem với _order bằng chuỗi sequence phẩy id, field sequence Integer default 10, cùng các field ngay diem_cong ghi_chu; phần XML là list editable bottom trong đó field sequence mang widget handle đặt ở cột đầu, tiếp theo là ngay diem_cong ghi_chu; dưới cùng ghi chú kéo dòng thì Odoo cập nhật sequence và lưu, thiếu _order thì thứ tự không giữ

Hình 1: Hai mảnh bắt buộc. Model có sequence (Integer) và _order = 'sequence, id' để danh sách luôn sắp theo số thứ tự. View đặt widget="handle" lên chính field sequence, để cột đầu tiên. Thiếu _order thì kéo xong thứ tự không được giữ — đây là lỗi hay gặp nhất.

Lưu ý quan trọng: widget="handle" gắn lên đúng field sequence, và nên để nó ở cột đầu để cái tay nắm nằm bên trái. Field này thường không cần cho người dùng thấy giá trị số — widget biến nó thành biểu tượng kéo.

Cái tay nắm trên danh sách thật

Sau khi nâng cấp module, mở tab "Lịch sử điểm" của thẻ VIP-0001. Mỗi dòng giờ có một biểu tượng kéo ở đầu:

Ảnh chụp giao diện Odoo thật của tab Lịch sử điểm dạng bảng, mỗi dòng có biểu tượng tay nắm gồm các chấm dọc ở cột đầu bên trái, ba dòng theo thứ tự Mua 5 ly cà phê điểm 50, Đổi combo Tết điểm 30, Giới thiệu bạn điểm 40, dưới cùng là link Thêm một dòng

Hình 2: Trước khi kéo. Biểu tượng tay nắm (các chấm dọc ⣿) hiện ở đầu mỗi dòng nhờ widget="handle". Thứ tự hiện tại: "Mua 5 ly cà phê" → "Đổi combo Tết" → "Giới thiệu bạn" (sequence 10, 20, 30). Giữ chuột lên tay nắm rồi kéo là đổi được vị trí.

Kéo, và thứ tự đổi

Bây giờ giữ tay nắm của dòng cuối ("Giới thiệu bạn") và kéo lên đầu bảng:

Ảnh chụp giao diện Odoo thật cùng tab sau khi kéo, thứ tự các dòng đã đổi thành Giới thiệu bạn điểm 40 ở trên cùng, rồi Mua 5 ly cà phê điểm 50, rồi Đổi combo Tết điểm 30; các biểu tượng tay nắm vẫn ở cột đầu

Hình 3: Sau khi kéo. "Giới thiệu bạn" đã nhảy lên đầu. Khi bạn thả, Odoo tính lại giá trị sequence cho các dòng bị ảnh hưởng và lưu — nên lần mở sau thứ tự này vẫn giữ nguyên (nhờ _order='sequence, id'). Không có một dòng code JavaScript nào phải viết.

Điều đáng chú ý: bạn không hề đụng tới con số sequence bằng tay. Kéo–thả là thao tác của người dùng, còn Odoo lo phần dịch nó thành các giá trị số và ghi xuống. Đó là toàn bộ "phép thuật" của widget handle.

Vài lưu ý thực chiến

  • _order là bắt buộc. Thiếu nó, danh sách sắp theo id — kéo xong nhìn thì đổi, nhưng mở lại là về thứ tự cũ. Rất dễ tưởng widget hỏng.
  • default cho sequence. Đặt default=10 (hoặc một số) để dòng tạo mới không rơi vào sequence=0 chen lên đầu. Nhiều module dùng bước nhảy 10 để chèn giữa dễ.
  • Handle hợp cả list độc lập lẫn One2many trong form. Ở đây là One2many (dòng con của thẻ), nhưng một list cấp cao nhất (ví dụ các giai đoạn CRM) cũng dùng đúng cách này.
  • Thứ tự là dữ liệu chung, không theo người dùng. sequence là một cột trong bảng — ai kéo cũng đổi cho mọi người. Nếu cần thứ tự riêng từng người thì phải mô hình khác, không phải handle.

Ba ý mang về

  1. Widget handle = field sequence (Integer) + _order='sequence, id' + <field name="sequence" widget="handle"/> để cột đầu. Ba mảnh, không cần JavaScript.
  2. Kéo–thả tay nắm, Odoo tự tính lại sequence và lưu; _order đảm bảo thứ tự được giữ giữa các lần mở — thiếu nó là lỗi phổ biến nhất.
  3. Handle dùng cho cả One2many trong form lẫn list độc lập; default cho sequence giúp dòng mới không chen lên đầu, và nhớ rằng thứ tự là dữ liệu dùng chung cho mọi người.

Tay nắm lo phần sắp xếp. Bài sau ta gom một chùm widget nhỏ mà hữu dụng cho việc hiển thị trạng thái: Phần sau về badge, priority (sao) và boolean_toggle (công tắc) — ba widget khiến field trạng thái và cờ đúng/sai trực quan hơn nhiều.