Ở 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:

with_context tạo recordset mới với ngữ cảnh thay đổi trong Odoo 19: không sửa env gốc trả về recordset mới gắn env mới context cộng dồn; đổi ngôn ngữ hiển thị, order.with_context lang vi_VN chấm state cho nhãn dịch sang tiếng Việt; cờ hệ thống active_test thấy cả bản ghi đã lưu trữ, P.search_count list chỉ active True, P.with_context active_test False search_count list gồm cả archived; đổi múi giờ hoặc công ty, rec.with_context tz Asia Ho Chi Minh, rec.with_company cong_ty; truyền cờ tuỳ biến để đổi hành vi code, def action_xu_ly self nếu self.env.context get bo_qua_kiem_tra đọc cờ, rec.with_context bo_qua_kiem_tra True action_xu_ly truyền cờ

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:

with_context chạy thật trên database blog19: một active_test mặc định lọc active True đổi cờ thấy cả archived, P.search_count list cho 30, P.with_context active_test False search_count list cho 33 cộng 3 đã lưu trữ; env gốc không đổi with_context tạo env mới, env.context get active_test cho None; hai đổi lang cùng field nhãn dịch khác nhau, so.with_context lang en_US chấm state cho draft Quotation sale Sales Order, so.with_context lang vi_VN chấm state cho draft Báo giá sale Đơn bán hàng, country.with_context lang vi_VN chấm name cho Việt Nam en là Vietnam; ba cờ tuỳ biến và cộng dồn context, r bằng P.with_context my_flag True tz Asia Ho Chi Minh, r.with_context them_flag X chấm env.context cho my_flag True them_flag X tz Asia Ho Chi Minh giữ key cũ

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_context thay 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 mong rec tự đổ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 ý

  1. with_context trả về recordset mới với context đã đổi, không sửa env gốc (đo thật: env.context gốc vẫn None); context cộng dồn khi dùng dạng keyword.
  2. 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ằng self.env.context.get(...).
  3. 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.