Ở bài env ta biết env.context là dict ngữ cảnh. Sức mạnh thật sự nằm ở chỗ đổi được nó — chạy một lời gọi ORM dưới ngữ cảnh khác: ngôn ngữ khác, thấy cả bản ghi đã lưu trữ, hay truyền một cờ điều khiển hành vi. Công cụ đó là with_context. Nối tiếp env, bài này mổ xẻ cách đổi ngữ cảnh.
with_context tạo recordset MỚI
Điểm cốt lõi: with_context không sửa recordset/env gốc, mà trả về một recordset mới gắn với env mới có context đã đổi:

Hình 1: with_context(...) trả về recordset mới. Bốn cách dùng chính: đổi lang (ngôn ngữ hiển thị/dịch), cờ hệ thống active_test=False (thấy cả bản ghi đã lưu trữ), đổi tz/công ty, và truyền cờ tùy biến để method đọc bằng self.env.context.get(...). Context cộng dồn — thêm key mới, giữ key cũ.
Chạy thật — hai hiệu ứng rõ nhất
Đây là output thật trên db blog19:

Hình 2: Output thật. active_test=False: search cho 33 thay vì 30 — thấy thêm 3 đối tác đã lưu trữ (active=False). Quan trọng: env.context gốc vẫn None — with_context không sửa env gốc. Đổi lang: cùng field state, en_US cho "Quotation/Sales Order", vi_VN cho "Báo giá/Đơn bán hàng"; tên quốc gia "Vietnam" → "Việt Nam". Và context cộng dồn: thêm them_flag vẫn giữ my_flag và tz cũ.
Ba tình huống dùng nhiều nhất
1. active_test — thao tác trên bản ghi đã lưu trữ
Truy vấn Odoo mặc định lọc active=True (giấu bản ghi đã lưu trữ). Muốn thấy/xử lý cả bản ghi archived:
tat_ca = self.env['res.partner'].with_context(active_test=False).search([])
2. lang — dịch và báo cáo đa ngôn ngữ
In hóa đơn cho khách nước ngoài bằng ngôn ngữ của họ:
self.with_context(lang=partner.lang or 'en_US').render_report()
3. Cờ tùy biến — điều khiển hành vi method
Truyền cờ để đổi cách method chạy (bỏ qua một kiểm tra, đổi chế độ):
# Trong method:
if self.env.context.get('bo_qua_gui_mail'):
return
# Khi gọi:
record.with_context(bo_qua_gui_mail=True).action_xac_nhan()
Nhiều tính năng Odoo dùng cờ context kiểu này (mail_create_nosubscribe, tracking_disable, default_<field>...).
Với công ty và người dùng
Ngoài with_context, có các anh em chuyên biệt:
with_company(cong_ty): đổi công ty hiện tại (cho dữ liệu đa công ty).with_user(user): chạy dưới danh nghĩa người dùng khác (phân quyền của họ).sudo(): bỏ qua phân quyền — là bài sau.
Lưu ý
with_contextthay TOÀN BỘ context nếu bạn truyền dict qua tham số vị trí, nhưng dạng keyword (with_context(key=value)) thì cộng dồn — thường dùng dạng keyword.- Không đổi được env gốc: luôn dùng kết quả trả về (
rec2 = rec.with_context(...)), đừng mongrectự đổi. - Cờ context truyền xuống sâu: gọi method trên recordset đã đổi context thì mọi lời gọi con thừa hưởng context đó — tiện nhưng cẩn thận rò rỉ cờ.
Nhớ ba ý
with_contexttrả về recordset mới với context đã đổi, không sửa env gốc (đo thật:env.contextgốc vẫnNone); context cộng dồn khi dùng dạng keyword.- Ba cờ hay dùng:
active_test=False(thấy archived — đo thật 30→33),lang(dịch — "Quotation"→"Báo giá"), và cờ tùy biến đọc bằngself.env.context.get(...). - Anh em chuyên biệt:
with_company(đa công ty),with_user(danh nghĩa người khác),sudo(bỏ quyền); luôn dùng kết quả trả về, đừng mong recordset gốc tự đổi.
Có một cách đổi env đặc biệt quyền lực và nguy hiểm: chạy bỏ qua phân quyền. Phần sau đi vào sudo() — khi nào cần, khi nào là lỗ hổng bảo mật, và cách dùng đúng.