Ở bài trước ta viết trường computed. Có một tham số nhỏ trên đó quyết định rất nhiều: store. Nó là ranh giới giữa "trường tính lại mỗi lần đọc" và "trường lưu hẳn xuống cơ sở dữ liệu". Chọn sai là hoặc trang chậm, hoặc không tìm kiếm/sắp xếp được. Bài này mổ xẻ đánh đổi đó.

Hai chế độ, một tham số

  • Không store (mặc định): trường computed tính lại mỗi lần đọc, không có cột trong bảng SQL. Không tốn chỗ lưu, không tốn gì khi ghi.
  • store=True: giá trị được lưu xuống CSDL (có cột thật), Odoo tính lại và ghi khi trường nguồn (@api.depends) đổi. Đọc nhanh, tìm kiếm/sắp xếp được — đổi lại tốn chỗ và tốn khi ghi.

Bảng so sánh trường computed store True hay không trong Odoo 19: khai không store mặc định tính mỗi lần đọc không có cột SQL, tong bằng fields.Float compute _compute_tong; store True lưu xuống cơ sở dữ liệu tính lại khi trường nguồn đổi, tong bằng fields.Float compute _compute_tong store True; bảng so sánh cột trong bảng SQL không store là Không còn store True là Có, tìm kiếm search domain không store là Không trừ khi tự viết hàm search còn store True là Được, sắp xếp order by không store Không store True Được, dùng trong read_group không store Không store True Được, chi phí đọc không store tính mỗi lần store True đọc sẵn nhanh, chi phí ghi không store 0 store True tính và ghi khi nguồn đổi; chọn store True khi cần tìm sắp báo cáo theo trường ngược lại để không store

Hình 1: Đánh đổi. store=True cho bạn cột SQL → tìm kiếm, sắp xếp, read_group đều được, và đọc nhanh (không tính lại). Không store thì nhẹ khi ghi nhưng không lọc/sắp bằng SQL (trừ khi tự viết hàm search). Quy tắc: cần tìm/sắp/báo cáo theo trường → store=True; chỉ hiển thị → để không store.

Bằng chứng thật

Đây là output thật trên db blog19 cho thấy khác biệt rõ ràng:

Bằng chứng thật store vs không store trên database blog19: amount_total của sale.order là computed store True, SO._fields amount_total cho compute True store True; vì store True nên có cột thật trong bảng SQL, psql SELECT column_name từ bảng sale_order cho amount_total và amount_untaxed là các cột thật; nên tìm kiếm và sắp xếp theo amount_total chạy được, SO.search amount_total lớn hơn 1000000 order amount_total desc trả về 51.060.000 19.800.000 12.430.000; trường computed không store ví dụ activity_exception_icon, SO.search activity_exception_icon khác False báo ValueError Cannot convert sale.order.activity_exception_icon to SQL because it is vì không có cột nên không lọc SQL được

Hình 2: Bằng chứng thật. amount_total (compute + store=True) có cột thật trong bảng sale_order (kiểm bằng information_schema), nên search([('amount_total','>',1000000)], order='amount_total desc') chạy ngon, trả về các đơn lớn nhất. Còn trường computed không store như activity_exception_icon thì search theo nó nổ ValueError: Cannot convert ... to SQL — vì không có cột để lọc.

Khi nào dùng store=True

Chọn store=True khi bạn cần ít nhất một trong các điều này:

  • Tìm kiếm theo trường (bộ lọc trên giao diện, domain).
  • Sắp xếp danh sách theo trường.
  • Gom nhóm/báo cáo (read_group, pivot, biểu đồ).
  • Đọc rất thường xuyên trên bảng lớn (đọc sẵn nhanh hơn tính lại).

Ví dụ điển hình: amount_total phải store=True vì báo cáo doanh thu gom nhóm theo nó (nhớ read_group).

Khi nào để KHÔNG store

  • Trường chỉ hiển thị trên form, không lọc/sắp (ví dụ một chuỗi ghép để show).
  • Giá trị phụ thuộc ngữ cảnh (người dùng, công ty hiện tại) — lưu xuống là sai cho người khác.
  • Trường đổi liên tục mà ít khi cần tìm — lưu chỉ tổ tốn ghi.

Cái giá của store=True

store=True không miễn phí:

  • Tốn chỗ: thêm một cột dữ liệu.
  • Tốn khi ghi: mỗi lần trường nguồn đổi, Odoo tính lại và ghi xuống CSDL — với bảng lớn và @api.depends xuyên nhiều bản ghi, việc này có thể nặng.
  • Rủi ro lệch: nếu @api.depends khai thiếu, giá trị lưu sẽ cũ mà không ai biết (không store thì luôn tính mới nên không lệch). Đây là lý do @api.depends đủ và đúng càng quan trọng khi store=True.

Nạp lại giá trị lưu (recompute)

Khi bạn sửa công thức của một trường store=True, giá trị cũ trong CSDL không tự đổi. Cần nâng cấp module để Odoo tính lại:

odoo -d blog19 -u ten_module --stop-after-init

Hoặc trong dev, gọi Model._recompute_fields() / cập nhật qua giao diện kỹ thuật.

Nhớ ba ý

  1. store=True lưu trường computed xuống CSDL (có cột SQL) → tìm kiếm, sắp xếp, read_group được; không store thì tính mỗi lần đọc, không lọc SQL được (đo thật: search cho ValueError: Cannot convert to SQL).
  2. Chọn store=True khi cần tìm/sắp/báo cáo hoặc đọc nhiều trên bảng lớn; để không store khi chỉ hiển thị hoặc giá trị phụ thuộc ngữ cảnh.
  3. store=True tốn chỗ + ghi, và rủi ro lệch nếu @api.depends khai thiếu; sửa công thức thì phải -u module để tính lại giá trị đã lưu.

Trường computed lưu được rồi, có một loại trường "tính toán" đặc biệt gọn hơn: related. Phần sau khám phá trường related — rút gọn một chuỗi quan hệ (partner_id.country_id.name) thành một trường phẳng, mà không cần viết hàm compute.