Khi bấm "Tạo mới", form đã điền sẵn nhiều thứ: ngày là hôm nay, người phụ trách là bạn, trạng thái là "nháp". Những giá trị điền sẵn đó là default — và Odoo có ba cách đặt chúng, từ đơn giản đến linh hoạt. Nối tiếp onchange (phản ứng khi đổi), bài này bàn giá trị ban đầu khi tạo.
Ba cách đặt default

Hình 1: Ba cách. default tĩnh (default=0, default='bac') cho hằng số. default callable (default=fields.Date.context_today, default=lambda self: ...) cho giá trị tính lúc tạo — chú ý truyền hàm, không gọi (). default_get override cho logic động/phức tạp theo ngữ cảnh, trả về dict.
Chạy thật trên module
Tôi thêm cả ba loại vào model quan.the.thanh.vien, nâng cấp, rồi kiểm tra. Output thật:

Hình 2: Output thật. default_get(...) trả về dict đầy đủ: diem=0 và hang_the='bac' (tĩnh), ngay_cap=date(2026,9,17) — đúng hôm nay (callable), và name='TV-2026-###' (từ default_get, theo năm). create({}) không truyền field nào vẫn ra bản ghi đầy đủ vì Odoo áp mọi default.
default callable — cái bẫy dấu ngoặc
Lỗi kinh điển: viết default=fields.Date.today() (có ()) thay vì default=fields.Date.context_today (không gọi). Nếu gọi (), giá trị được tính một lần lúc nạp module và đóng băng — mọi bản ghi mới đều mang cùng một ngày (ngày khởi động server)! Truyền hàm (không ()) để Odoo gọi nó mỗi lần tạo:
# ✗ SAI: tính một lần, đóng băng
ngay = fields.Date(default=fields.Date.today())
# ✓ ĐÚNG: truyền hàm, gọi mỗi lần tạo
ngay = fields.Date(default=fields.Date.context_today)
user_id = fields.Many2one('res.users', default=lambda self: self.env.user)
default_get — khi cần logic
Dùng default_get khi giá trị mặc định phụ thuộc nhiều yếu tố hoặc cần truy vấn:
@api.model
def default_get(self, fields_list):
res = super().default_get(fields_list) # LUÔN gọi super trước
if 'hang_the' in fields_list:
# ví dụ: khách VIP thì mặc định hạng vàng
partner = self.env.context.get('default_partner_id')
if partner and self.env['res.partner'].browse(partner).customer_rank > 10:
res['hang_the'] = 'vang'
return res
Luôn gọi super().default_get() trước để giữ các default khác, rồi mới chỉnh dict.
Đặt default qua context
Odoo còn cho đặt default tạm thời qua context với khóa default_<tên_field>. Đây là cách các nút "Tạo" trên giao diện truyền giá trị:
# Mở form tạo thẻ với hạng mặc định = vàng (chỉ lần này)
action = {
'type': 'ir.actions.act_window',
'res_model': 'quan.the.thanh.vien',
'view_mode': 'form',
'context': {'default_hang_the': 'vang', 'default_diem': 50},
}
Rất tiện cho nút "Tạo con" (tạo bản ghi liên kết với giá trị điền sẵn theo bản ghi cha).
Thứ tự ưu tiên
Khi nhiều nguồn cùng đặt default, thứ tự (cao → thấp): default_get override > context default_<field> > default= trên field. default_get chạy sau cùng nên có tiếng nói quyết định — đó là lý do nó dùng cho logic phức tạp.
default vs onchange vs computed
Ba cơ chế "tự điền" dễ lẫn:
- default: giá trị ban đầu lúc tạo mới (một lần).
- onchange: cập nhật khi người dùng đổi trường khác trên form.
- computed: giá trị luôn tính từ trường khác (mọi lúc).
Nhớ ba ý
- default tĩnh (
default=0) cho hằng; default callable (default=fields.Date.context_today, không()) cho giá trị động;default_getcho logic phức tạp — đo thật:create({})rangay_cap= hôm nay,hang_the='bac',name='TV-2026-###'. - Cái bẫy:
default=fields.Date.today()(có()) đóng băng ngày; phải truyền hàm để gọi mỗi lần tạo. - Thứ tự ưu tiên
default_get> contextdefault_<field>>default=; luôn gọisuper().default_get()trước khi override.
Bản ghi tạo xong cần một cái "tên hiển thị" để người dùng thấy trong ô Many2one, danh sách. Phần sau đi vào display_name — cách Odoo quyết định hiển thị tên một bản ghi, và cách tùy biến nó.