Bài trước ta viết một file SCSS và khai nó vào manifest. Nhưng chuyện gì thực sự xảy ra sau đó? Trình duyệt không tải file .scss của bạn — nó tải một tệp .min.css khổng lồ gộp hàng trăm file lại. Cơ chế đứng sau là assets bundling: Odoo gom mọi file khai trong một bó (bundle), biên dịch SCSS, minify, rồi phục vụ ở một URL duy nhất. Bài này mổ xẻ nó — và tôi sẽ grep tận trong bó thật để chứng minh file của mình nằm đó.

Khai file vào một bó trong manifest

Trong __manifest__.py, key assets là một dict: mỗi khoá là một bó, giá trị là danh sách file (hỗ trợ glob **):

Ảnh chụp đoạn mã nền tối, key assets trong manifest với bó web.assets_backend chứa danh sách file css scss glob js xml của module; phần liệt kê các bó thường gặp gồm web.assets_backend cho quản trị, web.assets_frontend cho website, point_of_sale assets_pos cho POS, web.assets_common dùng chung; phần bộ chỉ dẫn chèn tương đối gồm prepend, after, before, replace, remove; kèm chú thích bó là tập file gộp thành một tệp minify phục vụ ở web assets, nạp css scss js xml, thêm bớt file cần nâng cấp

Hình 1: assets với bó web.assets_backend. Danh sách nhận .css, .scss (được biên dịch), .js (OWL), .xml (template QWeb-OWL), và glob **/*.scss để gom nhiều file. Bên dưới là các bó khác và bộ chỉ dẫn chèn tương đối.

Nhiều bó cho nhiều nơi

Điểm cốt lõi: khai file vào bó nào thì chỉ nơi đó nạp nó. Vài bó hay gặp:

  • web.assets_backend — khu quản trị (form, list, kanban...). CSS/JS cho giao diện nội bộ vào đây.
  • web.assets_frontend — website công khai. Style cho trang khách xem.
  • point_of_sale._assets_pos — màn hình điểm bán hàng POS.
  • web.assets_common — dùng chung mọi nơi.

Đặt sai bó là lỗi âm thầm điển hình: file CSS backend mà nhét vào assets_frontend thì khu quản trị không bao giờ thấy nó, và ngược lại — không báo lỗi, chỉ là "không có tác dụng".

Bằng chứng: file của tôi nằm trong bó

Nói suông thì dễ. Tôi đăng nhập, tìm thẻ <link> tới bó CSS backend, tải nó về và grep:

Ảnh chụp phiên kiểm tra: link stylesheet chứa web.assets_backend gom vào bó chung web.assets_web.min.css tại /web/assets/7282d38; fetch bó đó trả status 200, kích thước 1 231 058 byte đã minify gộp hàng trăm file; kiểm tra chứa chuỗi o_quan_form trả về true, chứa mã màu 6f4e37 trả về true nghĩa là biến SCSS ca-phe-nau đã biên dịch; đoạn tìm thấy trong bó là quy tắc o_quan_form oe_title h1 màu 6f4e37 border-left 4px

Hình 2: Bó backend thật (web.assets_web.min.css) nặng 1.231.058 byte — đã minify, gộp hàng trăm file. Grep trong đó thấy đúng .o_quan_form .oe_title h1{color: #6f4e37; ...} — chính quy tắc SCSS của module (biến $ca-phe-nau đã biên dịch thành #6f4e37). File .scss của tôi thật sự bị nuốt vào bó và biến thành CSS thường.

Đây là chứng cứ cho toàn bộ cơ chế: bạn khai .scss, Odoo biên dịch → gộp → minify → phục vụ dưới một URL có băm phiên bản. Trình duyệt tải một tệp, không phải hàng trăm.

Chèn tương đối và thứ tự

Thứ tự file trong list chính là thứ tự nạp (quan trọng với CSS: quy tắc sau đè quy tắc trước). Khi cần xen file của bạn vào giữa các file có sẵn của Odoo, dùng bộ chỉ dẫn:

  • ('prepend', path) — nạp trước toàn bộ bó.
  • ('after', target, path) / ('before', target, path) — nạp ngay sau/trước một file cụ thể.
  • ('replace', target, path) — thay một file; ('remove', target) — gỡ một file khỏi bó.

Những cái này hiếm dùng nhưng cứu bạn khi cần ghi đè một style/JS của core mà không được sửa core.

Vài lưu ý

  • Thêm/bớt file cần -u. Sửa nội dung file thì Odoo build lại bó khi tải trang (chế độ dev), nhưng thay đổi danh sách trong manifest thì phải nâng cấp module để nạp lại.
  • URL có băm phiên bản (/web/assets/<hash>/...) — đổi file là đổi hash, nên trình duyệt luôn lấy bản mới, không kẹt cache.
  • Kiểm tra bằng DevTools hoặc fetch bó như trên: cách chắc chắn nhất để biết file đã vào bó chưa, thay vì đoán.

Ba ý mang về

  1. assets trong manifest gom file vào các bó; mỗi khoá (web.assets_backend, web.assets_frontend, ._assets_pos...) là một bó — Odoo biên dịch SCSS, gộp, minify thành một tệp phục vụ ở /web/assets/<hash>/<bó>.min.css.
  2. Khai đúng bó cho đúng nơi: backend cho quản trị, frontend cho website, POS cho điểm bán — đặt sai bó là "không có tác dụng" âm thầm.
  3. Thứ tự trong list = thứ tự nạp; dùng prepend/after/before/replace/remove để chèn tương đối; thêm/bớt file cần -u; kiểm bằng cách grep chính bó thật.

Xong chuyện giao diện và asset. Bài sau quay lại luồng điều hướng: Phần sau đào sâu window action — mọi trường của ir.actions.act_window (target, res_id, domain, context, view_id...) quyết định action mở ra cái gì và mở như thế nào.