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

Ba cách đặt giá trị mặc định khi tạo bản ghi mới trong Odoo 19 default và default_get: default tĩnh hằng số cố định, diem bằng fields.Integer default 0, hang_the bằng fields.Selection default bac; default callable tính lúc tạo như ngày hôm nay hoặc người dùng, ngay_cap bằng fields.Date default fields.Date.context_today truyền hàm không gọi ngoặc, user_id bằng fields.Many2one res.users default lambda self trả self.env.user; default_get là default động logic phức tạp theo ngữ cảnh, decorator api.model def default_get self fields_list, res bằng super default_get fields_list, nếu name trong fields_list và chưa có thì res name bằng TV cộng năm hiện tại cộng dấu thăng, return res trả dict tên field giá trị mặc định; thứ tự ưu tiên default_get động lớn hơn default field lớn hơn context default field

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:

Output default và default_get chạy thật trên module quan_ca_phe database blog19: gọi T.default_get với danh sách name diem ngay_cap hang_the trả về dict diem 0 là default tĩnh, hang_the bac là default tĩnh Selection, ngay_cap date 2026 9 17 là default callable bằng hôm nay, name TV-2026 kèm dấu thăng từ default_get động theo năm; tạo bản ghi create rỗng không truyền field nào thì Odoo áp mọi default, the.name trả TV-2026 dấu thăng, the.diem trả 0, the.hang_the trả bac, the.ngay_cap trả 2026-09-17 đúng ngày hôm nay; ghi chú default callable và default_get tính lúc tạo còn default tĩnh là hằng

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 ý

  1. default tĩnh (default=0) cho hằng; default callable (default=fields.Date.context_today, không ()) cho giá trị động; default_get cho logic phức tạp — đo thật: create({}) ra ngay_cap = hôm nay, hang_the='bac', name='TV-2026-###'.
  2. 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.
  3. Thứ tự ưu tiên default_get > context default_<field> > default=; luôn gọi super().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ó.