Nhiều thao tác cần người dùng nhập vài thông số rồi bấm chạy: cộng điểm hàng loạt, đổi trạng thái kèm lý do, xuất một báo cáo có tham số. Cái hộp thoại đó cần một model để chứa những gì người dùng gõ vào. Nhưng lưu vĩnh viễn thì phí — nhập xong là bỏ. Odoo giải quyết bằng TransientModel: một model mà bản ghi của nó là tạm, tự dọn định kỳ. Đó chính là wizard. Bài này mổ xẻ luồng cơ bản của một wizard cộng điểm.

TransientModel: model của dữ liệu tạm

Một wizard chỉ là một class kế thừa models.TransientModel thay vì models.Model. Field khai y như model thường, nhưng bản ghi được lưu vào bảng riêng và bị cơ chế vacuum dọn sau một thời gian:

Ảnh chụp đoạn mã nền tối models them_diem_wizard.py, class ThemDiemWizard kế thừa models.TransientModel với _name quan.them.diem.wizard, ba field diem_cong Integer default 10, the_ids Many2many tới thẻ thành viên, ghi_chu Char; method default_get gọi super rồi nếu active_model là thẻ thì nạp the_ids từ active_ids của context; method action_cong_diem lặp qua the_ids cộng diem_cong rồi trả ir.actions.act_window_close; kèm chú thích TransientModel lưu bảng riêng bị vacuum dọn, mở bằng action target new, default_get lấy bản ghi được chọn, method trả act_window_close để đóng

Hình 1: Wizard quan.them.diem.wizard là TransientModel. Field khai như thường. default_get điền sẵn khi mở hộp thoại — ở đây nạp the_ids từ active_ids trong context (các thẻ được chọn). action_cong_diem là việc nút "Lưu" gọi: cộng điểm cho từng thẻ, rồi trả act_window_close để đóng dialog.

Ba mảnh của luồng wizard:

  • TransientModel giữ dữ liệu tạm — không đụng bảng của model thật.
  • default_get + active_ids — khi mở wizard từ một danh sách/bản ghi, context mang theo active_ids; default_get đọc nó để nạp sẵn các bản ghi liên quan.
  • Method của nút — làm việc thật (đổi dữ liệu model chính), rồi trả một action: act_window_close để đóng, hoặc một act_window khác để đi tiếp.

Wizard bật lên và điền sẵn

Wizard mở dạng dialog (nhờ action target="new" ở bài window action). Nó tự điền thẻ đang xem vào ô "Thẻ áp dụng":

Ảnh chụp giao diện Odoo thật một hộp thoại Cộng điểm hàng loạt nổi lên, ô Số điểm cộng thêm giá trị 10, danh sách Thẻ áp dụng đã có sẵn thẻ VIP-0001 của Nguyễn Văn An hạng Bạc 150 điểm, ô Lý do trống, dưới cùng là nút Lưu và Huỷ bỏ

Hình 2: Wizard hiện dạng dialog. Ô "Số điểm cộng thêm" mặc định 10; "Thẻ áp dụng" đã có sẵn VIP-0001 — đó là default_get đọc active_ids và nạp vào. Người dùng chỉnh số, thêm lý do, bấm "Lưu" (gọi action_cong_diem), dialog đóng và điểm được cộng. "Huỷ bỏ" chỉ đóng, không lưu gì — vì đằng nào bản ghi wizard cũng là tạm.

Vòng đời một wizard — nhìn tận nơi

Để thấy rõ "bản ghi tạm", tôi chạy trực tiếp qua odoo shell:

Ảnh chụp phiên odoo shell: W là model wizard, W._transient trả về True nghĩa là bảng riêng tự dọn định kỳ; tạo wiz với context active_ids của VIP-0004 và diem_cong 10; wiz.the_ids.mapped name trả về VIP-0004 nghĩa default_get đã nạp từ active_ids; v4.diem trước là 0; gọi wiz.action_cong_diem trả ir.actions.act_window_close để đóng hộp thoại; v4.diem sau là 10 nghĩa wizard đã cộng điểm cho thẻ được chọn

Hình 3: _transient là True — xác nhận đây là bản ghi tạm. Tạo wizard với active_ids của VIP-0004, default_get nạp đúng thẻ đó vào the_ids. Gọi action_cong_diem: điểm 0 → 10, method trả act_window_close. Bản ghi wizard này rồi sẽ bị vacuum dọn — không để lại rác.

Vì sao dùng TransientModel, không phải Model thường

  • Không rác trong CSDL. Mỗi lần mở wizard tạo một bản ghi; nếu là Model thường, chúng tích lại mãi. TransientModel được vacuum dọn tự động.
  • Đúng ngữ nghĩa "tạm". Dữ liệu wizard là đầu vào cho một thao tác, không phải thực thể nghiệp vụ cần lưu. Model "tạm" phản ánh đúng điều đó.
  • Vẫn đủ sức mạnh của ORM. Wizard có field, quan hệ, method, view như model thường — chỉ khác vòng đời. the.diem += ... trong method wizard chạy bình thường (đây là method Python thật, không phải sandbox như server action code).

Ba ý mang về

  1. Wizard = TransientModel: model có bản ghi tạm, lưu ở bảng riêng và bị vacuum dọn định kỳ — hợp cho hộp thoại nhập liệu, không để rác trong CSDL.
  2. default_get + context active_ids nạp sẵn bản ghi được chọn khi mở wizard; mở dạng dialog bằng action target="new".
  3. Method của nút làm việc thật rồi trả action: act_window_close để đóng, hoặc một act_window khác để đi bước tiếp — và trong method wizard, đổi dữ liệu bằng gán thuộc tính bình thường (không phải sandbox).

Wizard một bước đã xong. Nhưng nhiều tác vụ cần nhiều bước — chọn, xác nhận, xem kết quả. Bài sau: Phần sau về wizard nhiều bước — cách một wizard dẫn người dùng qua từng màn hình bằng cách method trả về chính nó (hoặc một wizard khác) với dữ liệu mang theo.