Khi cần chèn logic vào lúc bản ghi được tạo, sửa, hay xoá — sinh mã tự động, kiểm tra điều kiện, ghi nhật ký — bạn ghi đè create, write, hoặc unlink. Nghe đơn giản, nhưng đây là một trong những chỗ dễ viết sai nhất trong Odoo, và cái sai thường không báo lỗi mà chỉ làm dữ liệu lệch âm thầm. Bài này dựng một model thật với cả ba override rồi chạy thử để chỉ ra cách làm đúng và những cái bẫy.
create: nhận DANH SÁCH vals, không phải một dict
Bẫy lớn nhất: trong Odoo hiện đại, create nhận một danh sách các dict vals (tạo hàng loạt), không phải một dict đơn. Bạn phải trang trí bằng @api.model_create_multi và lặp qua vals_list:
@api.model_create_multi # BẮT BUỘC
def create(self, vals_list):
for vals in vals_list: # sửa vals TRƯỚC khi tạo
if not vals.get('ma_tu_sinh'):
vals['ma_tu_sinh'] = sinh_ma()
records = super().create(vals_list) # gọi super để THẬT SỰ tạo
for rec in records: # hậu xử lý trên bản ghi ĐÃ tạo
rec.ten = (rec.ten or '') + ' ✓'
return records # PHẢI trả về recordset

Hình 1: Ba override đúng chuẩn. Điểm cần thuộc lòng: (1) create có @api.model_create_multi và lặp vals_list; (2) super() được gọi để thao tác thật sự xảy ra — quên là bản ghi không được tạo/sửa/xoá; (3) sửa vals trước super (để giá trị vào đúng lúc ghi), nhưng hậu xử lý field tính từ bản ghi thì sau super (khi đã có id); (4) create trả về recordset, unlink chặn bằng cách raise trước super.
Chạy thật: ba override cùng bắn
Cài model demo quan.nhat.ky.thao.tac với ba override trên vào module rồi chạy odoo shell:

Hình 2: Cả ba override chạy đúng. create nhận danh sách 3 vals, tạo 3 bản ghi trong một lệnh; ma_tu_sinh được đặt trước super (NK-00001/2/3), còn dấu ✓ được thêm sau super. write gọi super rồi trả về. unlink raise UserError khi gặp bản ghi đã khoá, và xoá bình thường bản ghi thường (3→2).
Ba cái bẫy làm dữ liệu sai âm thầm
- Quên
@api.model_create_multi: code cũ dùngdef create(self, vals)với một dict. Trong Odoo 19, khung gọicreatevới danh sách, nênvalscủa bạn thành một list —vals.get(...)nổ hoặc xử lý sai. Luôn dùngmodel_create_multi+ lặp. - Quên gọi
super(): nếu overridecreate/write/unlinkmà không gọisuper(), thao tác không thật sự xảy ra — bản ghi không được ghi, nhưng cũng không có lỗi. Đây là loại bug tệ nhất vì im lặng. - Đặt hậu xử lý sai phía super: field tính từ bản ghi (cần
id, cần giá trị đã lưu) phải xử lý sau super; còn giá trị muốn đưa vào lúc ghi phải đặt vàovalstrước super. Đảo ngược là giá trị vào sai thời điểm.
Vài lưu ý thêm
writethường gọi super trước rồi hậu xử lý, vì bạn muốn dữ liệu đã lưu rồi mới phản ứng. Nhưng nếu cần chặn một thay đổi, kiểm tra trước super rồi raise.unlinkchặn bằng cách raise trước super (như ví dụ). Cẩn thận:unlinkchạy trên recordset nhiều bản ghi, nên kiểmany(...)/for rec in selfchứ đừng giả định một bản ghi.- Đừng ghi đè để làm việc mà có cơ chế sẵn: ràng buộc dữ liệu nên dùng
@api.constrainshaymodels.Constraint; giá trị mặc định nên dùngdefault/default_get. Ghi đècreate/writechỉ khi thật cần chèn logic vào vòng đời. create/write/unlinkbỏ qua nếu recordset rỗng vẫn an toàn, nhưng hãy nhớ chúng luôn có thể nhận nhiều bản ghi — viết code theo hướng batch từ đầu.
Ba ý mang về
createphải có@api.model_create_multivà nhậnvals_list(danh sách dict) — đã chứng minh: một lệnhcreate([...])tạo 3 bản ghi cùng lúc. Lặp quavals_list, sửa vals trướcsuper(), hậu xử lý sau.- Luôn gọi
super()trong cả ba override — quên là thao tác không xảy ra mà không báo lỗi.createtrả về recordset;writetrả vềTrue;unlinktrả về kết quả super. - Đặt logic đúng phía super: giá trị đưa vào lúc ghi → trước super; phản ứng/field từ bản ghi đã tạo → sau super; chặn thao tác (
unlinkbản ghi khoá) → raise trước super. Nhớ mọi override chạy theo lô nhiều bản ghi.
Xong phần model, ta chuyển sang phần giao diện. Lần sau bắt đầu nhóm bài về view: Phần sau mổ xẻ bố cục form view — sheet, group, và cách sắp xếp field cho một biểu mẫu gọn gàng, chuẩn Odoo.