Bạn muốn kiểm tra nhanh: "có bao nhiêu partner là công ty?", "trường name có bắt buộc không?", "tạo thử một bản ghi xem nó ra sao?". Viết hẳn một module để thử mấy câu đó thì quá phí. Odoo có một công cụ gọn hơn nhiều: odoo shell — một dấu nhắc Python đã nạp sẵn toàn bộ ORM và gắn thẳng vào cơ sở dữ liệu.
Sau khi đã biết bật chế độ dev, đây là công cụ thứ hai bạn sẽ dùng hằng ngày.
Mở shell
Lệnh giống hệt lệnh chạy server, chỉ thêm chữ shell:

Hình 1: Mở odoo shell và vài thao tác ORM. Chú ý: shell nạp sẵn env (môi trường ORM) và self (mặc định là OdooBot, uid 1). Không cần import gì cả — cứ env['tên.model'] là có model để làm việc.
Ba biến có sẵn: env, self, model
env— môi trường ORM:env.uid(người đang chạy),env.user,env.cr(con trỏ CSDL),env.company. Lấy model bằngenv['res.partner'].self— một recordset khởi tạo sẵn, mặc định làres.users(1,)tức OdooBot.- Truy vấn qua model:
search,search_count,read,create,write,unlink.
Đây là output thật khi tôi chạy trên CSDL blog19 của loạt bài này (dữ liệu mẫu quán cà phê):

Hình 2: Output thật. search_count([]) cho 30 partner; search([('is_company','=',True)], limit=3) trả về recordset res.partner(1, 6, 10). P._fields['name'].type cho biết trường name kiểu char, required=False. Phần dưới là một vòng create → commit → tìm lại → unlink → commit chạy thật rồi dọn sạch (còn 0).
Miền lọc (domain): trái tim của search
search nhận một domain — danh sách các bộ ba (trường, toán tử, giá trị):
# Tất cả partner là công ty
env['res.partner'].search([('is_company', '=', True)])
# Partner ở TP.HCM VÀ có email (mặc định các điều kiện nối bằng AND)
env['res.partner'].search([
('city', 'ilike', 'Hồ Chí Minh'),
('email', '!=', False),
])
# Toán tử logic tường minh: '|' là HOẶC cho 2 điều kiện kế tiếp
env['res.partner'].search(['|', ('city', '=', 'Hà Nội'), ('city', '=', 'Đà Nẵng')])
Vài toán tử hay dùng: =, !=, in, not in, >, <, like (phân biệt hoa thường), ilike (không phân biệt), child_of. Domain rỗng [] nghĩa là lấy tất cả.
Ghi dữ liệu: đừng quên env.cr.commit()
Đây là cái bẫy khiến người mới bối rối nhất. Trong shell, mọi thao tác create/write/unlink chỉ nằm trong giao dịch (transaction) hiện tại. Nếu bạn thoát shell mà không commit, giao dịch bị rollback — mọi thay đổi bốc hơi:
p = env['res.partner'].create({'name': 'Khách thử', 'city': 'Đà Nẵng'})
env.cr.commit() # ← BẮT BUỘC: chốt xuống CSDL. Thiếu dòng này là mất trắng.
Khác với khi code trong module (Odoo tự commit ở cuối request), shell để bạn tự quyết định lúc nào chốt. Điều này có mặt lợi: thử một thao tác nguy hiểm mà chưa commit thì cứ Ctrl+D thoát ra là như chưa từng xảy ra.
Vài mẹo thực dụng
- Chạy với user thật, không phải OdooBot:
env = env(user=2)rồi thao tác — để kiểm tra quyền đúng như người dùng đó thấy. - Đổi công ty ngữ cảnh:
env['res.partner'].with_company(cid).search(...). - Chạy script không cần gõ tay: nối bằng heredoc —
docker exec -i odoo19 odoo shell -d blog19 <<'PY' ... PY. Cả loạt bài này đo dữ liệu thật bằng đúng cách đó. - Thử nghiệm an toàn: cứ thử thoải mái, chỉ cần không commit thì không có gì xuống CSDL.
Cảnh báo
Shell nối thẳng vào CSDL production là con dao hai lưỡi: một câu search([]).unlink() gõ nhầm là xoá sạch bảng. Nguyên tắc: thử trên CSDL sao chép trước, và chỉ commit() khi chắc chắn.
Nhớ ba ý
odoo shelllà REPL Python nạp sẵnenv,self(OdooBot) và toàn bộ ORM — nghịch dữ liệu mà khỏi viết module.searchchạy bằng domain (danh sách bộ ba điều kiện);[]là lấy tất cả,ilikekhông phân biệt hoa thường.- Ghi dữ liệu phải
env.cr.commit()mới xuống CSDL — mặt trái là chỗ an toàn để thử: không commit thì không có gì thay đổi.
Chạy được ORM rồi, bạn sẽ cần nhìn Odoo "nói" gì khi có chuyện. Phần sau học cách đọc log Odoo và chỉnh cấp log theo từng module để soi đúng thứ mình cần.