Cách debug tệ nhất mà ai cũng từng làm: rải print() khắp nơi, chạy lại, đọc log, xoá print, lặp lại. Python có công cụ tốt hơn nhiều — pdb, trình gỡ lỗi tương tác có sẵn, không cần cài gì. Nó cho bạn dừng chương trình đúng một dòng rồi soi mọi biến đang sống, kể cả recordset ORM. Sau khi đã biết đọc log, đây là bước debug sâu hơn.

Đặt điểm dừng: chỉ một dòng

Từ Python 3.7, đặt breakpoint cực gọn — gõ breakpoint() ngay chỗ cần dừng:

Cách dừng chương trình đúng chỗ với pdb trong Odoo 19: đặt breakpoint trong hàm action_confirm bằng cách gõ breakpoint mở đóng ngoặc thay cho pdb.set_trace cũ, đặt ngay trước dòng order.state bằng sale; chạy Odoo với workers bằng 0 và limit-time-real bằng 0 để pdb hoạt động ổn định, có thể dùng ipdb đẹp hơn qua biến môi trường PYTHONBREAKPOINT; bảng các lệnh pdb hay dùng gồm p in giá trị biến và pp in đẹp, chữ l xem code quanh dòng đang dừng, chữ w hoặc where xem ngăn xếp lời gọi, n và s chạy dòng kế hoặc bước vào hàm, c chạy tiếp tới breakpoint sau, a in tham số hàm hiện tại, q thoát

Hình 1: Đặt breakpoint() trong code (thay cho import pdb; pdb.set_trace() kiểu cũ) và bảng lệnh pdb hay dùng. Lưu ý phải chạy Odoo với --workers=0: nhiều worker sẽ khiến điểm dừng rơi vào tiến trình bạn không gõ lệnh được. Thêm --limit-time-real=0 để request không bị giết khi bạn đang dừng soi biến.

Một phiên pdb thật

Đây là phiên pdb thật tôi chạy trên môi trường Odoo với db blog19: dừng lại rồi soi dữ liệu ORM bằng chính cú pháp recordset:

Phiên pdb thật trên môi trường Odoo database blog19: chương trình đã dừng ở breakpoint tại dòng print done; tại dấu nhắc Pdb gõ p len cong_ty trả về 6; gõ p cong_ty.mapped name trả về danh sách sáu tên gồm Cà Phê Việt Chi nhánh Hà Nội, Công ty Cà Phê Tây Nguyên, hai Công ty TNHH Cà Phê Việt, Global Tech Corp, Nông trại Cà Phê Đắk Lắk; gõ p len don_hang trả về 15; gõ p don_hang giới hạn một phần tử chấm amount_total trả về 2.0; gõ p env.uid trả về 1; gõ c để chạy tiếp và in ra done

Hình 2: Phiên pdb thật. Tại dấu nhắc (Pdb), mọi biểu thức ORM đều chạy được: p len(cong_ty) cho 6 công ty, p cong_ty.mapped("name") in ra danh sách tên thật, p len(don_hang) cho 15 đơn hàng, p don_hang[:1].amount_total cho 2.0, p env.uid cho 1. Rồi c để chạy tiếp. Bạn soi được đúng dữ liệu thật trong luồng đang chạy — điều mà print không bao giờ làm được linh hoạt vậy.*

Các lệnh pdb cốt lõi

  • p <biến> / pp <biến>: in giá trị (pp = in đẹp, cho dict/list dài). Gõ được cả biểu thức: p order.partner_id.name.
  • l (list): xem code quanh dòng đang dừng để định vị.
  • w (where): xem ngăn xếp lời gọi — ai gọi hàm này, từ đâu.
  • n (next): chạy hết dòng hiện tại; s (step): bước VÀO hàm được gọi.
  • c (continue): chạy tiếp tới breakpoint() kế; q (quit): thoát.
  • a (args): in các tham số của hàm hiện tại.

Mẹo: gõ tên biến trần đôi khi bị nhầm với lệnh pdb (ví dụ biến tên n, c). Dùng p n cho chắc.

ipdb: bản đẹp hơn

ipdb là pdb tô màu, có tự động hoàn thành. Cài pip install ipdb rồi trỏ biến môi trường để breakpoint() tự dùng nó:

PYTHONBREAKPOINT=ipdb.set_trace odoo --workers=0 -d blog19

Không đổi một dòng code nào — cùng breakpoint(), chỉ khác trải nghiệm.

Lưu ý khi debug trong Docker

  • Container phải chạy có TTY (docker exec -it ... hoặc stdin_open: true + tty: true trong compose) để pdb nhận bàn phím.
  • Với server đang chạy nền, cách gọn là dừng nó và chạy tay docker exec -it odoo19 odoo --workers=0 ... khi cần debug.
  • Nhớ gỡ hết breakpoint() trước khi commit — có checker (như pylint-odoo) bắt được cả cái này.

Nhớ ba ý

  1. Gõ breakpoint() ngay dòng cần dừng — chương trình dừng lại, bạn soi mọi biến (kể cả recordset ORM) bằng p/pp.
  2. Chạy Odoo với --workers=0 --limit-time-real=0 để pdb bắt đúng luồng và không bị giết vì quá giờ.
  3. Lệnh cốt lõi: p (in), l (xem code), w (ngăn xếp), n/s (chạy tiếp/bước vào), c (tiếp); ipdb cho bản đẹp hơn qua PYTHONBREAKPOINT.

Debug được Python phía sau rồi, còn phía trình duyệt thì sao? Phần sau chuyển sang debug frontend: chế độ assets dev, source map, và cách soi lỗi JavaScript của giao diện web Odoo.