Se rendre au contenu

Architecture technique Odoo 19

Bloc 2 · Environnement dev — Article 3/4 Architecture technique Odoo 19 MVC, ORM, cycle de requête, structure d'un module — on pose les fondations théoriques…
26 avril 2026 par
Architecture technique Odoo 19
B.Mustapha

Bloc 2 · Environnement dev — Article 3/4

Architecture technique Odoo 19

MVC, ORM, cycle de requête, structure d'un module — on pose les fondations théoriques avant de coder. Et on crée notre premier module : le helpdesk qui nous accompagnera jusqu'à la fin de la série.

~16 minutes de lecture

Ce que tu vas apprendre

MVC Odoo

L'architecture Modèle-Vue-Contrôleur adaptée par Odoo et son ORM.

Cycle de requête

Ce qui se passe entre le clic utilisateur et l'affichage de la réponse.

Structure module

Les fichiers obligatoires et l'anatomie d'un module Odoo 19.

Scaffold helpdesk

Créer le squelette du module helpdesk avec la commande scaffold.

Prérequis
  • Avoir lu l'article T05 (première approche du développement)
  • Un environnement Odoo 19 fonctionnel (T01, T02 ou T03)
  • Un IDE configuré (T04) — optionnel mais recommandé
Le module fil rouge — À partir de cet article, on construit un module helpdesk (gestion de tickets de support) qui grandit à chaque article. Si tu suis toute la série, tu auras un module complet et fonctionnel à la fin.

L'architecture MVC d'Odoo

Odoo utilise une variante du pattern MVC classique. L'ORM est la couche centrale qui traduit les objets Python en tables PostgreSQL.

Architecture MVC Odoo 19 : Model, View, Controller et ORM

Le navigateur envoie des requêtes HTTP au Controller. Celui-ci interagit avec le Model (via l'ORM) et rend la View.

1 — Le MVC d'Odoo expliqué

Le pattern MVC (Model-View-Controller) sépare les données, la présentation et la logique. Odoo l'adapte à sa manière :

Couche Rôle Fichiers Langage
Model (M) Définit les données et la logique métier : champs, méthodes, contraintes, workflows models/*.py Python
View (V) Définit la présentation : formulaires, listes, kanban, recherche, menus, actions views/*.xml XML / QWeb
Controller (C) Gère le routage HTTP, l'authentification, l'exécution des actions controllers/*.py Python
La particularité Odoo — Dans la plupart des frameworks (Django, Rails...), tu écris les Controllers toi-même. Dans Odoo, tu n'écris presque jamais de Controller. Le framework fournit un Controller générique qui gère automatiquement les CRUD, la navigation et les actions. Ton travail se concentre sur le Model (données + logique) et la View (présentation).

2 — L'ORM : le cœur d'Odoo

L'ORM (Object-Relational Mapping) est la couche qui traduit les objets Python en tables PostgreSQL. Tu ne manipules jamais le SQL directement — l'ORM s'en charge.

Les 5 opérations fondamentales (CRUD + Search)

# Créer un enregistrement
ticket = self.env['helpdesk.ticket'].create({
    'name': 'Bug formulaire contact',
    'priority': 'high',
})

# Lire des champs
print(ticket.name)  # 'Bug formulaire contact'

# Rechercher des enregistrements
tickets = self.env['helpdesk.ticket'].search([
    ('priority', '=', 'high'),
    ('state', '=', 'open'),
])

# Modifier un enregistrement
ticket.write({'state': 'in_progress'})

# Supprimer un enregistrement
ticket.unlink()
Ce que l'ORM fait pour toi
  • Génère automatiquement les requêtes SQL (INSERT, SELECT, UPDATE, DELETE)
  • Gère le cache pour éviter les requêtes inutiles
  • Applique les droits d'accès (ACL + record rules)
  • Déclenche les contraintes et les onchange
  • Met à jour les champs calculés automatiquement
  • Gère les transactions (rollback en cas d'erreur)

Le mapping Modèle ↔ Table

Concept Python Concept PostgreSQL Exemple
Modèle (_name)Tablehelpdesk.tickethelpdesk_ticket
Champ (fields.*)Colonnefields.Char('name')VARCHAR
EnregistrementLigne (row)ticket.id = 42
RecordsetRésultat d'une requêteself.env['...'].search([])

3 — Le cycle de requête

Voici ce qui se passe quand un utilisateur clique sur "Nouveau ticket" dans Odoo :

1. NAVIGATEUR → Requête HTTP POST /web/dataset/call_kw
   Body: { model: "helpdesk.ticket", method: "create", args: [{...}] }

2. ODOO HTTP → Reçoit la requête, vérifie l'authentification (session cookie)

3. CONTROLLER → Route la requête vers la méthode ORM appropriée

4. SÉCURITÉ → Vérifie les droits d'accès (ACL) et les record rules

5. ORM → Exécute la méthode create() sur le modèle helpdesk.ticket
   ├─ Valide les contraintes Python et SQL
   ├─ Déclenche les @api.onchange et @api.depends
   └─ Génère le SQL : INSERT INTO helpdesk_ticket (...)

6. POSTGRESQL → Exécute le SQL, retourne l'ID du nouvel enregistrement

7. ORM → Retourne le recordset au controller

8. CONTROLLER → Sérialise la réponse en JSON

9. NAVIGATEUR → Reçoit la réponse, OWL met à jour l'interface
Tout est transparent — En tant que développeur, tu ne gères que l'étape 5 : écrire le modèle Python. Le framework s'occupe de tout le reste (routage, sécurité, SQL, sérialisation).

4 — Anatomie d'un module Odoo 19

Un module Odoo est un répertoire Python qui contient des fichiers organisés par convention :

odooskills_helpdesk/
├── __init__.py              # Imports Python du module
├── __manifest__.py          # Métadonnées (nom, version, dépendances)
├── models/
│   ├── __init__.py          # Imports des modèles
│   └── helpdesk_ticket.py   # Définition du modèle
├── views/
│   └── helpdesk_ticket_views.xml  # Vues (form, list, kanban, search)
├── security/
│   └── ir.model.access.csv  # Droits d'accès (ACL)
├── data/
│   └── helpdesk_data.xml    # Données initiales (séquences, catégories...)
├── static/
│   └── description/
│       └── icon.png         # Icône du module (128x128)
└── README.md                # Documentation

Les 2 fichiers obligatoires

1. __init__.py — Point d'entrée Python du module. Importe les sous-dossiers :

from . import models

2. __manifest__.py — Carte d'identité du module. Odoo le lit pour savoir comment installer le module :

{
    'name': 'OdooSkills Helpdesk',
    'version': '19.0.1.0.0',
    'category': 'Services/Helpdesk',
    'summary': 'Gestion de tickets de support technique',
    'author': 'OdooSkills',
    'website': 'https://www.odooskills.com',
    'license': 'LGPL-3',
    'depends': ['base', 'mail'],
    'data': [
        'security/ir.model.access.csv',
        'views/helpdesk_ticket_views.xml',
    ],
    'installable': True,
    'application': True,
}
Les clés essentielles du manifest
  • depends — Liste des modules dont celui-ci dépend. Ils seront installés automatiquement. base est toujours implicite mais c'est une bonne pratique de le déclarer.
  • data — Fichiers XML/CSV chargés à l'installation et à la mise à jour. L'ordre compte : les fichiers sont chargés séquentiellement.
  • application — Si True, le module apparaît dans la liste des Applications (avec une icône et une description).
  • licenseLGPL-3 pour Community, OEEL-1 pour Enterprise.

5 — Créer le module helpdesk avec scaffold

Odoo fournit une commande scaffold qui génère le squelette d'un module. On va l'utiliser pour créer notre module fil rouge.

# Linux
/opt/odoo19/venv/bin/python3 /opt/odoo19/odoo/odoo-bin scaffold odooskills_helpdesk /opt/odoo19/custom-addons/

# Windows
python C:\odoo19\odoo\odoo-bin scaffold odooskills_helpdesk C:\odoo19\custom-addons\

La commande crée :

custom-addons/odooskills_helpdesk/
├── __init__.py
├── __manifest__.py
├── controllers/
│   ├── __init__.py
│   └── controllers.py
├── demo/
│   └── demo.xml
├── models/
│   ├── __init__.py
│   └── models.py
├── security/
│   └── ir.model.access.csv
└── views/
    ├── templates.xml
    └── views.xml

Nettoyer et adapter le scaffold

Le scaffold génère du code générique. On le remplace par notre manifest propre.

Remplace __manifest__.py par :

{
    'name': 'OdooSkills Helpdesk',
    'version': '19.0.1.0.0',
    'category': 'Services/Helpdesk',
    'summary': 'Module fil rouge — Gestion de tickets de support',
    'description': """
        Module développé tout au long de la série d'articles techniques OdooSkills.
        Chaque article ajoute des fonctionnalités : modèles, vues, héritage, sécurité, etc.
    """,
    'author': 'OdooSkills',
    'website': 'https://www.odooskills.com',
    'license': 'LGPL-3',
    'depends': ['base'],
    'data': [
        'security/ir.model.access.csv',
    ],
    'installable': True,
    'application': True,
}

Remplace models/models.py par models/helpdesk_ticket.py :

from odoo import models, fields


class HelpdeskTicket(models.Model):
    _name = 'helpdesk.ticket'
    _description = 'Ticket de support'

    name = fields.Char(string='Sujet', required=True)

Mets à jour models/__init__.py :

from . import helpdesk_ticket

Mets à jour security/ir.model.access.csv :

id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_helpdesk_ticket,helpdesk.ticket.user,model_helpdesk_ticket,base.group_user,1,1,1,1
Pourquoi si peu de code ? C'est volontaire. Ce module est un squelette minimal qui fonctionne. On ajoutera des champs (T10), des relations (T11), des contraintes (T12), de l'héritage (T13) et des vues (T16) dans les articles suivants. Chaque article enrichit le module.

6 — Tester l'installation du module

Vérifie que le module s'installe correctement :

# Linux
/opt/odoo19/venv/bin/python3 /opt/odoo19/odoo/odoo-bin \
    -c /opt/odoo19/odoo.conf \
    -i odooskills_helpdesk \
    -d odoo19_dev \
    --stop-after-init

# Windows
python C:\odoo19\odoo\odoo-bin -c C:\odoo19\odoo.conf -i odooskills_helpdesk -d odoo19_dev --stop-after-init

Si tout est correct, tu vois :

INFO odoo19_dev odoo.modules.loading: Loading module odooskills_helpdesk
INFO odoo19_dev odoo.modules.loading: Modules loaded.

Vérifie dans Odoo :

  1. Active le mode développeur (?debug=1)
  2. Va dans Paramètres → Technique → Modèles
  3. Cherche helpdesk.ticket
  4. Tu devrais voir le modèle avec le champ name
Le module n'a pas de vues — C'est normal. On n'a défini qu'un modèle avec un seul champ. Odoo génère automatiquement des vues par défaut (form et list) basiques, mais on créera les nôtres dans l'article T16.
Changements v19 — Points d'attention pour l'architecture :
  • URL backend : le préfixe est /odoo/ (plus /web#). Le routage est géré par le nouveau système de chemins propres.
  • OWL natif : tout le frontend est en OWL (pas de jQuery). Les composants utilisent @odoo/owl.
  • Contraintes : les _sql_constraints sont remplacées par des attributs de classe : _unique_name = models.Constraint('UNIQUE(name)', "...")
  • Vues : tree est renommé list dans view_mode.

Récapitulatif

Concept En pratique
Model (M)Classe Python dans models/*.py — définit les données et la logique
View (V)Fichier XML dans views/*.xml — définit la présentation
Controller (C)Géré automatiquement par le framework (rarement écrit manuellement)
ORMCouche qui traduit Python ↔ SQL (create/read/write/unlink/search)
Manifest__manifest__.py — carte d'identité du module
Scaffoldodoo-bin scaffold nom_module chemin/

État du module helpdesk après cet article

odooskills_helpdesk/
├── __init__.py              ✓
├── __manifest__.py          ✓ (name, version, depends, application)
├── models/
│   ├── __init__.py          ✓
│   └── helpdesk_ticket.py   ✓ (1 modèle, 1 champ : name)
└── security/
    └── ir.model.access.csv  ✓ (accès CRUD pour base.group_user)

Pour aller plus loin

  • Odoo Shell : teste l'ORM directement : self.env['helpdesk.ticket'].create({'name': 'Test'})
  • psql : vérifie la table créée : \d helpdesk_ticket
  • Code source Odoo : explore odoo/odoo/models.py pour voir l'implémentation de l'ORM

Liens utiles

Télécharge le Guide Technique Odoo 19

Architecture, pièges v19, checklist premier module — tout dans un PDF gratuit.

Télécharger le guide
Première approche du développement Odoo 19