1. Remplacez le contrôleur par la version du laboratoire
La section 2 a préparé une première route. Relisez maintenant le fichier complet. La méthode accepte uniquement un GET, exige une session et utilise l’environnement ORM de la requête en cours.
from odoo import http
from odoo.http import request
class ElearningPortalController(http.Controller):
@http.route(
"/formations",
type="http",
auth="user",
website=True,
methods=["GET"],
)
def formations_page(self):
modules = request.env["elearning.module"].search([], order="name")
return request.render(
"elearning_portal.formations_page",
{"modules": modules},
)auth="user"exige une session Odoo connectée. Pour lire ce model, la personne doit aussi être un utilisateur interne membre d’un groupe couvert par l’ACL, icibase.group_user.website=Trueplace la route dans le contexte Website et permet au template d’utiliserwebsite.layout.request.env["elearning.module"]emploie l’utilisateur et le contexte de la requête en cours.search([], order="name")lit les formations autorisées et les classe par nom.
N’ajoutez pas sudo(). Si l’utilisateur ne possède pas le droit de lire elearning.module, l’accès doit rester refusé. Corrigez l’ACL ou utilisez le bon compte interne au lieu de contourner la règle.
À observer après le redémarrage : un GET sur /formations appelle formations_page(). Une personne sans session est envoyée vers la connexion. Un compte connecté mais non autorisé ne reçoit pas les formations.
2. Vérifiez les deux imports
Le fichier du contrôleur doit être importé par son package, puis ce package doit être importé à la racine de l’addon.
from . import mainfrom . import controllers
from . import modelsRésultat attendu : au prochain redémarrage d’Odoo, Python charge le contrôleur et enregistre la route. Un simple rechargement du navigateur ne suffit pas après une correction d’import.
3. Transmettez uniquement ce que le template doit parcourir
Le contexte contient la clé modules et le recordset des formations. Il ne contient ni clé notes, ni note_ids préparé à part, ni contenu de note.
Le recordset reste côté serveur. Le navigateur reçoit uniquement le HTML produit par QWeb à partir des expressions du template. À l’étape suivante, vous afficherez module.name et module.description, jamais module.note_ids.
La même formation garde ses relations dans le back-office. La page Website en présente seulement le nom, la description et un lien interne. Une relation disponible dans l’ORM n’est pas automatiquement publiée dans le HTML.
4. Utilisez l’URL actuelle du client web Odoo 19
L’action créée dans la section 1 possède l’external ID elearning_portal.elearning_module_action. Odoo 19 accepte cet external ID dans le chemin, suivi de l’identifiant numérique du record :
/odoo/action-elearning_portal.elearning_module_action/<ID_DE_LA_FORMATION>Dans QWeb, le record courant fournira cet identifiant :
<a class="btn btn-outline-primary mt-3"
t-att-href="'/odoo/action-elearning_portal.elearning_module_action/%s' % module.id">
Ouvrir la fiche interne
</a>action-elearning_portal.elearning_module_action indique à Odoo quelle action ir.actions.act_window charger. Le dernier segment indique quel record ouvrir. Comme l’action propose list,form et qu’un record précis est demandé, le client ouvre sa form view.
Le routeur actuel d’Odoo 19 représente une action et son record par des segments sous /odoo/. Gardez la forme vérifiée ci-dessus au lieu de construire un fragment avec id, model et view_type.
Le lien ne donne aucun droit. Il fonctionne seulement avec une session interne capable de lire elearning.module. Après un clic, Odoo recharge l’action, le record et ses views sous les droits de cette même session.
5. Faites un contrôle avant le template
custom_addons/elearning_portal/controllers/main.py— contrôlez la route — les valeurs attendues sontauth="user",website=Trueetmethods=["GET"].custom_addons/elearning_portal/controllers/main.py— contrôlez la recherche — l’expression attendue commence parrequest.env["elearning.module"], sanssudo().custom_addons/elearning_portal/controllers/main.py— contrôlez le contexte — la seule collection transmise est{"modules": modules}.custom_addons/elearning_portal/views/elearning_module_views.xml— contrôlez l’action — l’ID attendu estelearning_module_action.
Le contrôleur est importé, la route respecte la session et les ACL, aucune note n’est préparée pour le Website et la forme du lien cible une vraie fiche back-office Odoo 19.