Skip to Content

OWL vs QWeb en Odoo 17

OWL ou QWeb ? C'est la question que se posent tous les développeurs Odoo qui démarrent un nouveau composant ou un nouveau template en Odoo 17. Les deux coexistent dans le même écosystème, mais ils ne servent pas les mêmes objectifs. Ce guide vous donne les critères clairs pour choisir — et des exemples de code pour les deux.

1. OWL vs QWeb : deux technologies, deux rôles

La confusion entre OWL et QWeb vient du fait qu'ils utilisent tous les deux une syntaxe XML similaire et que QWeb est aussi le langage de templates d'OWL. Mais ils opèrent à des niveaux complètement différents.

Framework JavaScript
OWL
Odoo Web Library. Composants réactifs côté client. Équivalent d'un React ou Vue.js conçu par Odoo.
VS
Moteur de templates
QWeb
Langage de templates XML. Rendu côté serveur (Python) pour les rapports, factures, emails.
💡
À retenir d'emblée

QWeb est un moteur de templates. OWL est un framework de composants. QWeb peut être utilisé côté serveur (Python) ET côté client (comme langage de templates dans OWL). OWL, lui, est exclusivement côté client.

2. QWeb : le moteur de templates côté serveur

QWeb est le langage de templates XML d'Odoo. Il existe depuis les premières versions d'Odoo et reste le standard pour tout ce qui est rendu côté serveur : rapports PDF, templates d'emails, factures, bons de livraison.

Sa syntaxe repose sur des directives préfixées t- qui contrôlent la logique du rendu :

report_facture_template.xmlQWeb XML
<template id="report_facture_document">
  <t t-foreach="docs" t-as="doc">
    <div class="page">

      <!-- Affichage conditionnel -->
      <t t-if="doc.partner_id.country_id.code == 'CA'">
        <p class="text-muted">TPS / TVQ applicables</p>
      </t>

      <!-- Interpolation de variable -->
      <h2><span t-field="doc.name"/></h2>
      <p>Client : <span t-field="doc.partner_id.name"/></p>

      <!-- Boucle sur les lignes de facture -->
      <table class="table">
        <t t-foreach="doc.invoice_line_ids" t-as="line">
          <tr>
            <td><span t-field="line.name"/></td>
            <td><span t-field="line.quantity"/></td>
            <td><span t-field="line.price_subtotal"/></td>
          </tr>
        </t>
      </table>
    </div>
  </t>
</template>

Les directives QWeb essentielles

  • t-if / t-elif / t-else — affichage conditionnel
  • t-foreach / t-as — boucle sur une liste ou un recordset
  • t-field — affiche un champ Odoo avec formatage automatique (dates, montants)
  • t-out — affiche une expression Python (remplace l-ancien t-raw en Odoo 17)
  • t-call — inclut un autre template QWeb
  • t-attf- — interpolation dans les attributs HTML (t-attf-class="badge {{state}}")

3. OWL : le framework de composants côté client

OWL (Odoo Web Library) est le framework JavaScript réactif d'Odoo, introduit massivement en Odoo 14 et devenu le standard exclusif en Odoo 17. Il remplace complètement l'ancien framework Widgets basé sur Backbone.js.

OWL s'inspire de React et Vue : composants avec état, réactivité, cycle de vie. Sa grande particularité est d'utiliser QWeb comme langage de templates, mais exécuté côté client en JavaScript.

static/src/components/projet_widget.jsJavaScript + OWL
/** @odoo-module **/

import { Component, useState, onMounted } from "@odoo/owl";
import { registry } from "@web/core/registry";

class ProjetStatusWidget extends Component {
    // Template QWeb associé (défini dans un fichier XML séparé)
    static template = "mon_module.ProjetStatusWidget";

    static props = {
        record: Object,
        readonly: { type: Boolean, optional: true },
    };

    setup() {
        // État réactif du composant
        this.state = useState({
            isExpanded: false,
            loading: false,
        });

        onMounted(() => {
            console.log("Widget monté pour le projet :", this.props.record.data.name);
        });
    }

    toggleExpand() {
        this.state.isExpanded = !this.state.isExpanded;
    }

    get statusClass() {
        const state = this.props.record.data.state;
        const classes = {
            brouillon: "badge-secondary",
            en_cours: "badge-warning",
            termine: "badge-success",
            annule: "badge-danger",
        };
        return classes[state] || "badge-secondary";
    }
}

// Enregistrement du widget dans le registre Odoo
registry.category("fields").add("projet_status", {
    component: ProjetStatusWidget,
});
static/src/components/projet_widget.xmlQWeb (template OWL)
<?xml version="1.0" encoding="utf-8"?>
<templates>
  <t t-name="mon_module.ProjetStatusWidget">
    <div class="projet-status-widget">

      <!-- Badge de statut cliquable -->
      <span
        t-attf-class="badge {{statusClass}}"
        t-on-click="toggleExpand">
        <t t-out="props.record.data.state"/>
      </span>

      <!-- Panneau expandable (réactif) -->
      <t t-if="state.isExpanded">
        <div class="status-detail mt-2">
          <small>Responsable : <t t-out="props.record.data.responsable_id[1]"/></small>
        </div>
      </t>

    </div>
  </t>
</templates>

4. Tableau comparatif complet

Critère OWL QWeb (serveur)
Exécution Navigateur (JavaScript) Serveur (Python)
Cas d'usage principal Widgets, interfaces dynamiques, dashboards Rapports PDF, emails, factures, étiquettes
Réactivité ✅ Mise à jour automatique de l'UI ❌ Rendu statique au moment de la génération
Accès aux données Via RPC / services Odoo Accès direct aux modèles Python
Langage de template QWeb JS (côté client) QWeb Python (côté serveur)
Courbe d'apprentissage Moyenne à élevée (JS, composants, cycle de vie) Faible à moyenne (XML + Python basique)
Debugging DevTools navigateur, console JS Logs serveur Python, mode debug Odoo
Héritage / override patch() depuis @web/core/utils/patch t-inherit et t-inherit-mode

5. Quand utiliser QWeb (côté serveur)

✅ Utilisez QWeb pour
  • 📄Rapports PDF (factures, bons de commande, étiquettes)
  • 📧Templates d'emails transactionnels
  • 🖨️Documents imprimables (devis, bons de livraison)
  • 🌐Pages web statiques Odoo Website
  • 📋Templates de contrats ou documents légaux
❌ N'utilisez pas QWeb pour
  • 🚫Interfaces interactives avec état
  • 🚫Widgets de formulaire personnalisés
  • 🚫Dashboards avec mise à jour en temps réel
  • 🚫Composants avec logique JavaScript complexe
Exemple : rapport PDF personnalisé avec QWebPython + XML
# Dans models/projet_client.py
def action_print_rapport(self):
    return self.env.ref('mon_module.action_rapport_projet').report_action(self)

# Dans le manifest : ajouter l'action de rapport
# Dans report/rapport_projet.xml :

6. Quand utiliser OWL

✅ Utilisez OWL pour
  • 🎛️Widgets de champs personnalisés dans les formulaires
  • 📊Dashboards et tableaux de bord interactifs
  • 🔔Composants de la barre systray (notifications)
  • 🗂️Vues Kanban ou Gantt personnalisées
  • Toute interface nécessitant de la réactivité
❌ N'utilisez pas OWL pour
  • 🚫Générer des PDFs ou des emails
  • 🚫Traitement de données côté serveur
  • 🚫Simples modifications de templates de rapports
  • 🚫Personnalisations qui n'impliquent pas l'interface

7. OWL et QWeb ensemble : le cas des templates OWL

C'est là que la confusion s'installe souvent : dans OWL, les templates de composants sont écrits en QWeb, mais exécutés en JavaScript dans le navigateur. C'est du QWeb client-side, distinct du QWeb serveur.

⚠️
Distinction critique

Un template QWeb dans un fichier XML de rapport (ir.ui.view de type qweb) est rendu par Python. Un template dans static/src/ avec t-name est un template OWL, rendu par JavaScript. Même syntaxe, exécution radicalement différente.

Les directives QWeb disponibles diffèrent légèrement entre les deux contextes. Par exemple, t-field (qui formate les champs Odoo) n'est disponible que côté serveur. Côté OWL, on utilise t-out avec des getters JavaScript.

En Odoo 17, si vous touchez à quoi que ce soit dans static/src/, vous êtes dans le monde OWL. Si vous travaillez dans report/ ou views/ avec des templates liés à des rapports, vous êtes dans QWeb serveur.

8. Guide de décision rapide

Arbre de décision : OWL ou QWeb ?
Q1
Est-ce que votre besoin implique une interface utilisateur dans le navigateur ? (formulaire, widget, dashboard, bouton interactif)
↳ Non → QWeb serveur (rapport, email, document imprimable)
↳ Oui → Question suivante
Q2
L'interface doit-elle réagir à des interactions utilisateur ou afficher des données dynamiques ? (clics, mise à jour en temps réel, état local)
↳ Non → QWeb client simple (héritage d'un template existant suffit)
↳ Oui → OWL
Q3
Devez-vous créer un widget de champ personnalisé dans un formulaire Odoo ?
↳ Oui → OWL (composant enregistré dans le registry fields)

9. Erreurs de débutant à éviter

ErreurContexteSolution
Utiliser t-field dans un template OWL OWL (client) Remplacer par t-out avec un getter JS
Modifier un rapport QWeb avec patch() OWL Utiliser t-inherit dans un XML de vues
Oublier /** @odoo-module **/ dans un fichier JS OWL OWL L'ajouter en première ligne de chaque fichier JS
Ne pas déclarer le fichier XML de template dans le manifest OWL L'ajouter dans 'assets''web.assets_backend'
Tenter d'accéder à this.env.model sans le service approprié OWL Injecter le service via useService('orm')

Pour aller plus loin : la documentation officielle OWL en Odoo 17, la référence QWeb côté client, et la documentation des rapports QWeb serveur. Pour les composants OWL avancés, le dépôt GitHub officiel d'OWL contient des exemples commentés.

Un projet Odoo avec des développements sur mesure ?

Notre équipe maîtrise OWL, QWeb, Python et l'écosystème Odoo 17 de A à Z. Modules personnalisés, widgets avancés, intégrations API : discutons de votre besoin.

Parler à un développeur Odoo

Commencez à écrire ici ...

Créer un module Odoo personnalisé de A à Z