Ở bài trước ta thêm một tab vào form Liên hệ bằng view inheritance. Nhưng có một sắc thái quan trọng chưa nói: khi bạn kế thừa một view, bạn đang sửa nó tại chỗ hay tạo một bản mới dựa trên nó? Hai ý định đó tương ứng với hai chế độ: extension và primary. Chọn sai là hoặc vô tình đổi form của cả hệ thống, hoặc không đổi được gì. Bài này phân biệt hai chế độ bằng một form biến thể thật.
Ba trường hợp kế thừa view
<!-- 1) EXTENSION (mặc định khi có inherit_id): SỬA view gốc TẠI CHỖ -->
<field name="inherit_id" ref="base.view_partner_form"/> <!-- mode ngầm = extension -->
<!-- 2) PRIMARY có inherit_id: TẠO view MỚI dựa trên gốc; gốc KHÔNG đổi -->
<record id="view_the_form_gon" model="ir.ui.view">
<field name="inherit_id" ref="view_the_form"/>
<field name="mode">primary</field> <!-- ← điểm mấu chốt -->
<field name="arch" type="xml">
<xpath expr="//notebook" position="replace"/> <!-- bỏ tab -->
<xpath expr="//chatter" position="replace"/> <!-- bỏ chatter -->
</field>
</record>
<record id="action_the_gon" model="ir.actions.act_window">
<field name="view_id" ref="view_the_form_gon"/> <!-- dùng biến thể -->
</record>

Hình 1: Ba trường hợp. (1) extension (mặc định khi có inherit_id): các phép sửa áp thẳng vào view gốc — mọi action dùng view gốc đều thấy. (2) primary + inherit_id: tạo một view mới kế thừa từ gốc; view gốc không đổi, biến thể chỉ dùng ở nơi bạn chỉ định. (3) không inherit_id: view độc lập, luôn là primary (đây là view "gốc rễ" như form chính của ta).
Xem thật: form biến thể "gọn", form gốc nguyên vẹn
Mình tạo một view primary kế thừa form chính nhưng bỏ notebook và chatter — làm một màn "nhập nhanh", rồi gắn nó vào một action riêng. Mở cùng thẻ VIP-0001 qua action đó:

Hình 2: Form biến thể (mode primary). Cùng thẻ VIP-0001, nhưng gọn hơn: không còn notebook (tab Lịch sử điểm/Ghi chú) và không có chatter — vì view primary đã replace chúng. Quan trọng nhất: form gốc (mở qua menu chính) vẫn đầy đủ notebook + chatter — biến thể primary không hề đụng tới nó. Hai form, một model, hai action khác nhau.
Nếu mình dùng extension thay vì primary ở đây, việc replace notebook/chatter sẽ xoá chúng khỏi form chính luôn — mọi người mở thẻ đều mất tab và chatter. primary khoanh vùng thay đổi vào đúng biến thể.
Khi nào dùng cái nào
- Dùng
extension(mặc định) khi bạn muốn bổ sung/sửa view có sẵn cho mọi người dùng nó: thêm một field vào form đơn hàng, thêm cột vào list sản phẩm. Đây là 90% trường hợp. - Dùng
primarykhi bạn muốn một biến thể riêng dùng ở một action/ngữ cảnh cụ thể, không ảnh hưởng bản gốc: một form "nhập nhanh", một list rút gọn cho một menu phụ, một form chỉ-đọc cho vai trò khác. primarycũng cần thiết khi kế thừa dây chuyền: nếu bạn muốn kế thừa tiếp từ một view đã là primary (biến thể của biến thể), lớp giữa phải là primary để làm "gốc" cho lớp sau.
Cơ chế bên dưới
extension: Odoo gộparchcủa view kế thừa vào view gốc khi dựng view cuối cùng. Không có view record "mới" nào được dùng riêng — chỉ có view gốc đã được vá.primary: view kế thừa trở thành một view độc lập cóarchriêng (đã gộp từ gốc + phép sửa của nó). Bạn tham chiếu nó bằngview_idtrong action, hoặc để nó làm gốc cho các extension khác.- Kiểm tra thật: view chính
view_the_formcómode=primary(khônginherit_id→ gốc rễ); view thêm tab partner cómode=extension; biến thể gọn cómode=primary+inherit_id→ view mới, gốc không đổi.
Vài lưu ý Odoo 19
modemặc định làextensionkhi cóinherit_id,primarykhi không có — nên chỉ khaimode=primarytường minh cho trường hợp (2).- Biến thể
primaryphải được trỏ tới (quaview_idcủa action) mới dùng được — khácextensiontự động áp. - Đừng lạm dụng
primary: mỗi biến thể là một view phải bảo trì. Nếu chỉ cần ẩn/hiện theo điều kiện, dùnginvisible/groupstrên cùng một form gọn hơn. replacetrongprimaryan toàn hơn trongextensionvì phạm vi bị khoanh vào biến thể — nhưng vẫn phải cẩn thận với các extension khác cùng nhắm view gốc.
Ba ý mang về
extension(mặc định) sửa view gốc tại chỗ — mọi nơi dùng view đó đều thấy;primarytạo một view mới dựa trên gốc mà gốc không đổi, chỉ dùng ở nơi được trỏ tới.- Đã chứng minh: view
primarykế thừa form chính,replacenotebook + chatter → một form "gọn" cho action riêng; form gốc vẫn đầy đủ. Kiểm chứngmode: gốc=primary (không inherit), tab partner=extension, biến thể=primary+inherit. - Chọn:
extensionđể bổ sung cho mọi người (90% ca);primarycho biến thể riêng theo action, hoặc làm gốc cho kế thừa dây chuyền.modemặc định theo có/khônginherit_id.
Nói tới sửa view, có một thao tác tinh tế hay cần: di chuyển hay thay một field đã có sang chỗ khác. Phần sau đi sâu thay thế và di chuyển field trong view — dùng position="move", replace, và các mẹo tránh vỡ view khi làm vậy.