Trường quan hệ nhiều bản ghi — One2many (dòng đơn hàng) và Many2many (thẻ, ưu đãi) — không ghi như trường thường. Bạn không thể order.order_line = [1, 2, 3]. Odoo dùng một bộ lệnh đặc biệt để nói rõ: thêm dòng nào, sửa dòng nào, gỡ dòng nào. Đó là Command (0..6). Mở màn chương "trường quan hệ", đây là kỹ năng bạn dùng mỗi khi thao tác dòng con bằng code.

Bảy lệnh Command

Trường O2m/M2m nhận một list các Command, mỗi Command là một thao tác:

Bảng Command 0 đến 6 trong Odoo 19 thêm bớt sửa bản ghi con trong One2many và Many2many: trường One2many Many2many nhận list các Command thay vì gán trực tiếp; mã 0 Command.create tạo bản ghi con mới, mã 1 Command.update id tạo sửa bản ghi con id, mã 2 Command.delete id xoá hẳn bản ghi con dùng cho One2many, mã 3 Command.unlink id gỡ liên kết không xoá bản ghi, mã 4 Command.link id thêm liên kết tới bản ghi có sẵn dùng cho Many2many, mã 5 Command.clear gỡ hết liên kết, mã 6 Command.set list ids thay toàn bộ bằng đúng danh sách ids; ví dụ from odoo.fields import Command, tạo thẻ CMD-001 với lich_su_ids là list Command.create hai dòng điểm cộng 50 và uu_dai_ids là list Command.link ud1 gắn ưu đãi có sẵn

Hình 1: Bảy Command. Mã 0 create (tạo dòng con mới), 1 update (sửa dòng con), 2 delete (xoá hẳn — cho O2m), 3 unlink (gỡ liên kết, giữ bản ghi), 4 link (thêm liên kết tới bản ghi có sẵn — cho M2m), 5 clear (gỡ hết), 6 set (thay toàn bộ bằng danh sách ids). Odoo 19 có API tên rõ ràng: Command.create(), Command.link()... thay cho tuple số khó nhớ (0,0,{}).

Chạy thật — cả bảy thao tác

Model quan.the.thanh.vien có lich_su_ids (One2many các dòng điểm) và uu_dai_ids (Many2many ưu đãi). Tôi chạy các Command thật trên db blog19:

Command chạy thật trên module quan_ca_phe database blog19: tạo thẻ CMD-001 với lich_su_ids là list hai Command.create và uu_dai_ids là list Command.link ud1, kết quả lich_su bằng 2 và uu_dai bằng danh sách Giảm 10 phần trăm; Command.link ud2 cho uu_dai bằng Giảm 10 phần trăm và Free topping là mã 4 thêm; Command.unlink dòng 1 cho lich_su còn 1 là Mua lần 2 gỡ 1 dòng One2many; Command.set list ud2 cho uu_dai bằng Free topping là mã 6 thay toàn bộ; Command.update ls với ghi_chu bằng ĐÃ SỬA cho lich_su ghi_chu bằng ĐÃ SỬA là mã 1 sửa dòng con; mọi thao tác trên One2many Many2many đều qua Command không gán trực tiếp list id

Hình 2: Output thật. Tạo thẻ kèm 2 dòng lịch sử (Command.create) và 1 ưu đãi (Command.link) cùng lúc. Rồi Command.link thêm ưu đãi thứ 2 → có 2. Command.unlink gỡ 1 dòng lịch sử → còn 1. Command.set([ud2]) thay toàn bộ M2m thành đúng 1 ưu đãi. Command.update sửa ghi chú dòng con thành "ĐÃ SỬA". Mọi thao tác đều đúng như tên lệnh.

Import Command

Odoo 19 khuyến khích dùng API tên thay cho tuple số:

from odoo.fields import Command

# Cách MỚI (rõ ràng):
'order_line': [Command.create({'product_id': 1, 'product_uom_qty': 2})]

# Cách cũ (tuple số — vẫn chạy nhưng khó đọc):
'order_line': [(0, 0, {'product_id': 1, 'product_uom_qty': 2})]

Ánh xạ: Command.create = (0,0,{}), update = (1,id,{}), delete = (2,id), unlink = (3,id), link = (4,id), clear = (5,), set = (6,0,[ids]).

Phân biệt hai cặp dễ nhầm

  • Command.delete(id): xoá hẳn bản ghi con khỏi CSDL. Dùng cho One2many (dòng con thuộc về cha, xoá là đúng).
  • Command.unlink(id): chỉ gỡ liên kết, bản ghi vẫn tồn tại. Dùng cho Many2many (ưu đãi vẫn còn, chỉ không áp cho thẻ này nữa).

Dùng delete trên M2m là xoá nhầm bản ghi dùng chung — cẩn thận!

  • Command.link(id): gắn tới bản ghi đã có (M2m — chọn ưu đãi có sẵn).
  • Command.create({...}): tạo bản ghi mới (O2m — thêm dòng lịch sử mới).

Dùng ở đâu?

  • create/write model cha với dòng con cùng lúc (như Hình 2).
  • Nút hành động thêm/bớt dòng bằng code.
  • Onchange điền dòng con tự động.
  • Import dữ liệu có quan hệ lồng nhau.

Trên giao diện, mỗi lần bạn thêm/xoá dòng trong bảng con của form rồi Lưu, Odoo cũng sinh đúng các Command này gửi xuống.

Nhớ ba ý

  1. Trường One2many/Many2many nhận list Command, không gán trực tiếp; Odoo 19 dùng Command.create/update/delete/unlink/link/clear/set (thay tuple số 0..6).
  2. Đo thật: Command.create thêm dòng O2m, Command.link gắn M2m, Command.set([ids]) thay toàn bộ, Command.unlink/update gỡ/sửa dòng con.
  3. delete(2) xoá hẳn (cho O2m) vs unlink(3) chỉ gỡ liên kết (cho M2m) — nhầm là xoá nhầm bản ghi dùng chung; link(4) gắn bản ghi có sẵn vs create(0) tạo mới.

Xong thao tác trên quan hệ nhiều bản ghi. Có một loại trường đặc biệt khác hay dùng: danh sách lựa chọn. Phần sau đi vào trường Selection — khai lựa chọn tĩnh và cách sinh lựa chọn động bằng hàm.