Người dùng tick vài dòng trong danh sách, mở menu ⚙ Tác vụ, chọn một lệnh — và cả loạt bản ghi được xử lý cùng lúc. Xoá hàng loạt, đổi trạng thái cả lô, cộng điểm cho mọi thẻ đã chọn. Cơ chế đứng sau gọn hơn bạn tưởng: lựa chọn của người dùng được đóng gói thành active_ids, và code của bạn nhận về một recordset chứa toàn bộ — chỉ việc lặp.

Server action gắn vào danh sách

Để một lệnh xuất hiện trong menu Tác vụ, ta khai một ir.actions.server bind vào model, khai binding_view_types để nó hiện ở list (và/hoặc form):

Ảnh chụp đoạn mã nền tối khai record ir.actions.server id action_server_tri_an tên Cộng 5 điểm tri ân, model_id trỏ model_quan_the_thanh_vien, binding_model_id cũng trỏ model đó, binding_view_types là list phẩy form, state là code, và field code chứa vòng lặp for the in records gọi the write đặt diem bằng diem cộng 5 kèm chú thích records là tất cả bản ghi đã tick từ active_ids và chú thích sandbox cấm the diem cộng gộp, rồi the message_post và log số thẻ; phần dưới chú thích trong server action có sẵn biến records record env log và records bằng env model browse của active_ids còn method model nhận self là recordset này

Hình 1: Server action "Cộng 5 điểm tri ân". binding_model_id + binding_view_types="list,form" khiến nó hiện trong menu Tác vụ của model. Trong code (state="code"), biến records đã là recordset chứa mọi dòng người dùng tick — Odoo dựng nó từ active_ids trong context. Lưu ý cái bẫy đã gặp: sandbox của server action cấm gán thuộc tính (the.diem += 5), phải dùng the.write({'diem': the.diem + 5}).

Điểm cốt lõi: bạn không viết vòng lặp lấy từng id rồi browse tay. Odoo đã gói lựa chọn thành active_ids và đưa cho bạn sẵn records. Một for the in records là chạm tới từng bản ghi; và vì records là recordset, bạn cũng có thể thao tác cả lô một phát: records.write({...}).

Tick vài dòng trong danh sách, thanh trên hiện "N đã chọn" và nút Tác vụ — mở ra thấy lệnh tuỳ biến nằm cạnh các lệnh dựng sẵn:

Ảnh chụp giao diện danh sách Thẻ thành viên của Odoo, thanh trên ghi Tất cả 4 đã chọn kèm nút In và nút Tác vụ, menu Tác vụ đang mở liệt kê Xuất, Nhân bản, Xoá màu đỏ, và ở nhóm dưới là Cộng 5 điểm tri ân; danh sách nhóm theo hạng hiện Bạc mở ngoặc 3 và Vàng mở ngoặc 1, khung tìm kiếm bên trái có HẠNG THẺ và TRẠNG THÁI

Hình 2: Chọn cả 4 thẻ ("Tất cả 4 đã chọn"), menu Tác vụ mở ra. Cạnh các lệnh có sẵn (Xuất, Nhân bản, Xoá) là "Cộng 5 điểm tri ân" — chính server action ta gắn. Bấm nó, Odoo truyền active_ids = 4 id đã chọn vào action. (Lệnh In ở nút riêng bên trái mới là nơi các report bám vào — hai menu tách nhau.)

Đo thật: một lần chạy, cả lô cùng đổi

Không đoán — chạy action với active_ids là 3 thẻ và đo điểm trước/sau:

Ảnh chụp phiên odoo shell nền tối, ids là danh sách ba id 1 46 48 là ba thẻ tick trên list, in điểm trước cho ra thẻ 1 là 150 thẻ 46 là 85 thẻ 48 là 0, gọi server action với context active_ids bằng ids rồi run, in điểm sau cho ra thẻ 1 là 155 thẻ 46 là 90 thẻ 48 là 5 nghĩa là cả ba đều cộng 5, delta là mỗi thẻ cộng 5 đồng loạt trong một lần chạy, và chú thích demo xong đã hoàn nguyên về 150 85 0

Hình 3: Gọi sa.with_context(active_ids=[1,46,48]).run() — đúng cách menu ⚛ truyền lựa chọn. Cả ba thẻ cùng +5 (150→155, 85→90, 0→5), delta đồng loạt bằng 5 trong một lần chạy. Đây là điểm mạnh của xử lý theo recordset: một câu lệnh áp cho cả tập. (Sau khi đo tôi đã hoàn nguyên điểm về 150/85/0 để giữ dữ liệu demo sạch.)

active_ids và active_id — đừng lẫn

  • active_ids (số nhiều): list id của mọi bản ghi đã chọn — dùng cho thao tác hàng loạt từ list.
  • active_id (số ít): id của bản ghi đang mở — có khi chạy từ form. Nếu code chỉ đọc active_id mà người dùng chọn nhiều dòng, bạn xử lý nhầm chỉ một cái.
  • active_model: tên model, hữu ích khi một server action dùng chung cho nhiều model.

Trong server action state="code", Odoo lo sẵn: records = mọi bản ghi (từ active_ids), record = bản ghi đầu. Còn nếu bạn viết một method trên model rồi cho action gọi, thì self chính là recordset nhiều bản ghi đó — for rec in self là lặp cả lô, không cần đụng tới context.

Ba ý mang về

  1. Chọn nhiều dòng + menu ⚙ Tác vụ = chạy action trên cả lô. Lựa chọn được gói thành active_ids; server action nhận sẵn records là recordset chứa tất cả — chỉ việc for the in records hoặc thao tác cả tập.
  2. Gắn action vào list bằng binding_model_id + binding_view_types="list"; sandbox state="code" cấm gán thuộc tính, dùng write().
  3. Phân biệt active_ids (nhiều, từ list) với active_id (một, từ form) — đọc nhầm là xử lý thiếu bản ghi; method model thì dùng thẳng self làm recordset.

Ta vừa gắn action vào menu Tác vụ của list. Nhưng còn gắn vào đâu nữa, và điều khiển nó hiện ở chỗ nào? Phần sau đào sâu binding — gắn action vào menu ngữ cảnh: binding_model_id, binding_view_types, và cả report lẫn server action cùng cơ chế.