Ở bài trước ta sửa cấu trúc view bằng code — mạnh nhưng nặng. Nhưng phần lớn nhu cầu "view cư xử khác nhau tuỳ chỗ mở" không cần đụng cấu trúc: chỉ cần truyền vài giá trị. Kênh đó là context — một cuốn từ điển đi kèm mọi action, chảy vào view và code. Một menu "Cấp thẻ Vàng" có thể mở form đã điền sẵn hạng Vàng, 100 điểm; một menu "Đơn của tôi" mở list đã lọc sẵn đơn của mình — tất cả nhờ context, không cần form/list riêng. Bài này cho một form tự điền sẵn để thấy context làm việc.

Đặt context trên action

<record id="action_the_vang" model="ir.actions.act_window">
  <field name="res_model">quan.the.thanh.vien</field>
  <field name="context">{
      'default_hang_the': 'vang',       <!-- điền sẵn khi TẠO MỚI -->
      'default_trang_thai': 'dang_dung',
      'default_diem': 100,
      'search_default_vip': 1,          <!-- bật filter 'vip' sẵn -->
  }</field>
</record>

Ảnh chụp mã XML nền tối định nghĩa một action act_window với res_model quan chấm the chấm thanh chấm vien và field context là một từ điển gồm default_hang_the bằng vang điền sẵn khi tạo mới, default_trang_thai bằng dang_dung, default_diem bằng 100, search_default_vip bằng 1 để bật filter vip sẵn, phần chú thích liệt kê các khoá context phép thuật gồm default field đặt giá trị mặc định khi tạo bản ghi mới, search_default filter bật sẵn filter hoặc group, form_view_ref list_view_ref ép dùng view cụ thể, đọc context tuỳ biến trong Python qua self chấm env chấm context chấm get, và truyền context xuống form con qua field Many2one với context default_is_company

Hình 1: context là một dict trên action. Các khoá "phép thuật" Odoo hiểu sẵn: default_<field> đặt giá trị mặc định của field khi tạo bản ghi mới; search_default_<filter> bật sẵn một filter/group của search view (đã gặp ở bài search view); form_view_ref/list_view_ref ép dùng một view cụ thể. Ngoài ra bạn tự đặt cờ tuỳ ý rồi đọc bằng self.env.context.get(...) trong code.

Xem thật: form mở ra đã điền sẵn

Gắn context trên vào một action "Cấp thẻ Vàng", bấm Tạo mới — form mở ra đã có giá trị:

Ảnh chụp form Odoo tạo mới thẻ qua action Cấp thẻ Vàng, breadcrumb ghi TV-2026-### Vàng, dải statusbar Mới cấp Đang dùng Hết hạn với Đang dùng đang sáng, thân form Mã thẻ TV-2026-###, cột Điểm và số dư hiển thị Điểm tích luỹ thanh tiến độ 100 phần trăm và Hạng thẻ Vàng, cột Thông tin thẻ Khách hàng trống Ghi chú nội bộ hiện vì thẻ Vàng và Ngày cấp 17 tháng 9, tất cả điền sẵn mà người dùng chưa gõ gì

Hình 2: Form tạo mới, tự điền sẵn nhờ context. Hạng thẻ = Vàng (default_hang_the), Điểm tích luỹ = 100% (default_diem), statusbar = Đang dùng (default_trang_thai) — tất cả đã có mà người dùng chưa gõ gì. Vì là hạng Vàng nên ô "Ghi chú nội bộ" cũng hiện (nhớ bài invisible điều kiện). Người dùng chỉ cần điền tên khách và lưu — nhanh hơn hẳn.

Điểm hay: cùng một model, cùng một form, nhưng mở qua action khác nhau cho trải nghiệm khác nhau. Một action "Cấp thẻ Bạc" chỉ cần đổi default_hang_the: 'bac' là xong — không cần form riêng.

default_get đọc context ra sao

Cơ chế bên dưới: khi tạo bản ghi mới, Odoo gọi default_get, và mỗi khoá default_<field> trong context thành giá trị mặc định của field đó. Kiểm chứng thật:

M = env['quan.the.thanh.vien'].with_context(
    default_hang_the='vang', default_diem=100, default_trang_thai='dang_dung')
M.default_get(['hang_the', 'diem', 'trang_thai'])
# → {'hang_the': 'vang', 'diem': 100, 'trang_thai': 'dang_dung'}

Đây là lý do default_ chỉ ảnh hưởng bản ghi mới — nó đi qua default_get, vốn chỉ chạy khi tạo.

Các dùng context hay gặp

  • default_<field>: điền sẵn khi tạo — cho menu chuyên biệt ("Cấp thẻ Vàng", "Đơn nháp").
  • search_default_<filter> / search_default_<groupby>: mở màn hình đã lọc/nhóm sẵn ("Đơn của tôi", "Việc quá hạn").
  • default_<x2m>: điền sẵn dòng con qua lệnh Command (ví dụ [(0,0,{...})]).
  • Cờ tuỳ biến: đặt {'from_special_flow': True} rồi đọc self.env.context.get('from_special_flow') trong create/write/compute để đổi hành vi theo ngữ cảnh.
  • Trên <field> Many2one/x2many: context="{'default_x': ...}" truyền xuống form con khi mở/tạo bản ghi liên quan.

Vài lưu ý Odoo 19

  • context là dict "cộng dồn" — action, view, và code chồng khoá lên nhau; khoá sau ghi đè khoá trước. Dùng with_context(...) để thêm mà không mất khoá cũ.
  • default_ chỉ chạy lúc tạo (qua default_get); muốn ép giá trị khi sửa thì phải làm trong write/onchange, không phải context.
  • Đừng nhét dữ liệu lớn vào context — nó đi kèm mọi request, giữ nhẹ (id, cờ, giá trị nhỏ).
  • Cờ tuỳ biến nên có tiền tố module (quan_ca_phe_...) để tránh đụng khoá của app khác.

Ba ý mang về

  1. context của action là kênh nhẹ đổi hành vi view mà không đụng cấu trúc: default_<field> điền sẵn khi tạo, search_default_<filter> bật lọc/nhóm sẵn, form_view_ref ép view. Đã chứng minh: action "Cấp thẻ Vàng" mở form đã điền Vàng/100/Đang dùng.
  2. default_ đi qua default_get nên chỉ tác động bản ghi mới (kiểm chứng: default_get trả đúng giá trị context); muốn ép khi sửa phải dùng write/onchange.
  3. Đặt cờ tuỳ biến trong context rồi đọc self.env.context.get(...) để đổi hành vi theo ngữ cảnh; truyền context xuống form con qua context trên field Many2one/x2many. Giữ context nhẹ, đặt tiền tố cho cờ riêng.

Nhắc tới search_default và lọc/nhóm, lần sau ta đào sâu chính chuyện sắp xếp và gom nhóm dữ liệu trong bảng: Phần sau mổ xẻ sắp xếp và nhóm trên list view — default_order, _order của model, nhóm nhiều cấp, và cách chúng phối hợp.