Ở hai phần trước controller của ta trả JSON. Nhưng nhiều lúc bạn cần trả về cả một trang web hoàn chỉnh — có header, footer, menu, đúng giao diện website của công ty: trang bảng xếp hạng, trang tra cứu, một landing page động. Odoo làm việc này bằng bộ đôi request.render + template QWeb. Bài này dựng thật một trang "bảng vàng thành viên" từ controller, cho nó mượn giao diện website của Odoo, rồi chụp trang thật đang chạy để thấy kết quả.
request.render: hàm trả về một trang
Thay vì trả chuỗi HTML tự viết, controller gọi request.render('module.template_id', {context}): đối số đầu là external id của một template QWeb, đối số sau là dict dữ liệu truyền vào template. Odoo render template thành HTML đầy đủ và trả về. Route đặt website=True để trang hoà vào hệ thống website (đa ngôn ngữ, theme, chỉnh sửa được sau này).

Hình 1: Controller (trên) gọi request.render với external id của template và dữ liệu. Template QWeb (dưới) bắt đầu bằng <t t-call="website.layout"> — mượn nguyên bộ khung website — rồi đổ dữ liệu ra bằng t-esc/t-foreach. Template phải nằm trong file XML khai trong data của manifest.
t-call="website.layout": mượn cả header và footer
Đây là mấu chốt để trang trông "thuộc về" website chứ không phải một trang HTML trơ trọi. <t t-call="website.layout"> gọi tới template khung của module website; mọi thứ bạn đặt bên trong nó được nhét vào phần thân, còn header (logo, menu, giỏ hàng, nút Sign in) và footer (liên kết, mạng xã hội) do website.layout tự lo. Nếu không cần bộ khung website đầy đủ mà chỉ cần khung tối giản, có web.frontend_layout hoặc web.layout.
Chạy thật, trang hiện ra như một trang website đúng nghĩa:

Hình 2: Trang thật tại /quan_ca_phe/bang_vang. Header và footer là của website.layout (Your Logo, menu, Sign in, footer mạng xã hội) — ta không viết chúng. Phần giữa là nội dung template: tiêu đề, dòng "Tổng cộng 4 thẻ, 385 điểm", và bảng 4 thẻ sắp theo điểm giảm dần (VIP-0003 vàng 150 → VIP-0004 0). Dữ liệu thật từ CSDL, đổ ra bằng t-foreach.
Truyền dữ liệu và đổ ra template
Dict thứ hai của request.render chính là "biến" mà template đọc được. Trong template, các directive QWeb hiển thị chúng:
t-esc(hoặct-out) — in một giá trị, có escape HTML (an toàn với dữ liệu người dùng).<t t-esc="the.name"/>in tên thẻ.t-foreach/t-as— lặp qua một recordset hay list.<tr t-foreach="the_list" t-as="the">sinh một hàng cho mỗi thẻ; trong vòng lặp có sẵnthe_index(chỉ số),the_first,the_last.- Gọi thẳng hàm Python đơn giản được:
t-esc="len(the_list)",t-esc="tong_diem".
Vì the_list là recordset thật, trong template bạn truy cập field như trong code server: the.name, the.diem, the.hang_the. Không cần chuyển sang dict trước.
website=True cho bạn thêm gì
Đặt website=True trên route (thay vì chỉ type='http') kích hoạt tầng website:
- Đa ngôn ngữ: URL tự nhận tiền tố ngôn ngữ, template dịch được.
- Theme & chỉnh sửa: trang dùng đúng theme website hiện tại; người quản trị có thể mở trình soạn để sửa (với template phù hợp).
request.website: trong controller có sẵn đối tượng website hiện tại (công ty, ngôn ngữ, cấu hình).
Nếu chỉ cần một trang HTML nội bộ không liên quan website (ví dụ trang in, trang kỹ thuật), bỏ website=True và t-call web.layout là đủ nhẹ.
Vài lưu ý
- Template phải khai trong manifest. File XML chứa
<template>phải nằm trong danh sáchdatacủa__manifest__.py, và module phảidependswebsitenếut-call="website.layout"— thiếu là external id không phân giải được lúc cài. t-escescape,t-out/t-rawkhông. Với dữ liệu người dùng nhập, luônt-esc; chỉ dùngt-outvới field Html đãsanitize(như field Html ở phần 162).- Quyền vẫn quan trọng. Trang
auth='public'chạy bằng Public user; ở đây tôisudo()để đọc danh sách thẻ, và trong thực tế phải cân nhắc kỹ dữ liệu nào được lộ ra công khai.
Ba ý mang về
request.render('module.template_id', {ctx})biến controller thành trang web — trả về HTML đầy đủ từ một template QWeb, dữ liệu truyền qua dict thứ hai.<t t-call="website.layout">mượn header/footer của website để trang trông đúng giao diện; đổ dữ liệu bằngt-esc(có escape) vàt-foreach/t-as, truy cập field recordset trực tiếp (the.name).website=Truekích hoạt đa ngôn ngữ, theme,request.website; template phải khai trongdatacủa manifest và moduledependswebsitekhi dùngwebsite.layout.
Ta vừa dựng trang bằng controller + code. Có một cách khác không cần controller: khai thẳng trang bằng XML. Phần sau làm trang website khai bằng XML — dùng <template> với thuộc tính đặc biệt để Odoo tự tạo route, không phải viết một dòng Python nào.