Module 5 — Patterns Ezway & bonnes pratiques

Les conventions internes Ezway, la checklist code review, les pièges classiques et les patterns avancés.

3h Synthèse

🎯 Objectifs

1. Conventions de nommage Ezway

Module

Modèles

Champs

Méthodes Python

Vues XML

2. Checklist code review

Avant d'ouvrir une PR Ezway, vérifier point par point :

📦 Structure & manifest

🛡️ Sécurité

🐌 Performance

🧹 Qualité Python

🧪 Tests

3. Patterns avancés Ezway

Pattern : Récupérer un record par XML ID

# PAS : self.env['res.partner'].browse(42)   ← fragile
admin_partner = self.env.ref('base.partner_admin')

Pattern : Domain dynamique avec contexte

book_id = fields.Many2one(
    'library.book',
    domain="[('state', '=', 'available'), ('id', 'in', allowed_book_ids)]",
)
allowed_book_ids = fields.Many2many('library.book', compute='_compute_allowed')

Pattern : Action retournée par bouton (smart button)

def action_view_reservations(self):
    self.ensure_one()
    return {
        'type': 'ir.actions.act_window',
        'name': _('Réservations'),
        'res_model': 'library.reservation',
        'view_mode': 'list,form',
        'domain': [('book_id', '=', self.id)],
        'context': {'default_book_id': self.id},
    }
<div class="oe_button_box" name="button_box">
  <button name="action_view_reservations"
          type="object" class="oe_stat_button" icon="fa-book">
    <field name="reservation_count" widget="statinfo" string="Prêts"/>
  </button>
</div>

Pattern : Compute avec dépendance "inverse"

price_with_tax = fields.Float(compute='_compute_price', inverse='_inverse_price', store=True)

@api.depends('price', 'tax_id.amount')
def _compute_price(self):
    for rec in self:
        rec.price_with_tax = rec.price * (1 + (rec.tax_id.amount or 0) / 100)

def _inverse_price(self):
    # Permet d'écrire directement dans le champ computed → recalcule price
    for rec in self:
        if rec.tax_id:
            rec.price = rec.price_with_tax / (1 + rec.tax_id.amount / 100)

Pattern : Cron job

<record id="cron_overdue_reminders" model="ir.cron">
  <field name="name">Library: Rappels retards</field>
  <field name="model_id" ref="model_library_reservation"/>
  <field name="state">code</field>
  <field name="code">model.cron_send_overdue_reminders()</field>
  <field name="interval_number">1</field>
  <field name="interval_type">days</field>
  <field name="active" eval="True"/>
</record>
@api.model
def cron_send_overdue_reminders(self):
    overdue = self.search([
        ('state', '=', 'overdue'),
        ('reminder_sent', '=', False),
    ])
    for reservation in overdue:
        reservation._send_reminder_email()
        reservation.reminder_sent = True

Pattern : Logger plutôt que print

import logging
_logger = logging.getLogger(__name__)

class MyModel(models.Model):
    def do_stuff(self):
        _logger.info("Démarrage du process pour %s records", len(self))
        try:
            ...
        except Exception as e:
            _logger.error("Erreur lors du process : %s", e, exc_info=True)
            raise

4. Pièges connus en Odoo 19

PiègeConséquenceSolution
Utiliser attrs="..." en XML❌ Crash en v19 (supprimé)Utiliser invisible="..." directement
Décorateur @api.multiPas d'erreur mais inutileLe supprimer (par défaut depuis v13)
self.create({...}) (single dict)Warning + deprecatedself.create([{...}]) + @api.model_create_multi
OWL 1 syntax (useState sans destructuring)Composant non renduLire la doc OWL 2 attentivement
<tree> au lieu de <list>Fonctionne mais warningUtiliser <list> (v19 standard)
Tracking sans mail.threadAucun effetHériter ['mail.thread', 'mail.activity.mixin']

5. Workflow Ezway recommandé

Avant de commencer un dev

  1. Lire le ticket / brief en entier (rare mais arrive)
  2. Identifier les modules Ezway existants qui font déjà 70% du travail
  3. Discuter à voix haute (Slack ou IRL) le modèle de données avec un senior
  4. Mettre en place une branche : git checkout -b feat/library-isbn-validation

Pendant le dev

  1. Coder par petits commits atomiques (1 commit = 1 idée)
  2. Lancer Odoo en mode --dev=reload,qweb,xml pour itérer vite
  3. Tester en parallèle au moins le happy path dans l'UI
  4. Écrire au moins 1 test unitaire par méthode action_

Avant la PR

  1. Run la checklist code review (ci-dessus)
  2. git rebase sur main pour avoir un historique propre
  3. Tester l'install from scratch sur une base vierge
  4. Décrire la PR : but, changements, screenshots si UI, instructions de test

6. Cheatsheet débogage

Quand l'install plante

docker compose logs -f odoo | grep -i error
docker compose exec odoo odoo -d test19 -u library --stop-after-init -i base

Quand une vue plante

  1. Mode dev → ouvrir la vue dans Settings → Technical → Views
  2. Vérifier le XML, copier-coller dans un éditeur
  3. Reset la vue à la version originale (bouton "Reset to default")

Quand une méthode ne fait pas ce qu'on croit

import pdb; pdb.set_trace()        # pour débug ligne par ligne
# ou en mieux :
import ipdb; ipdb.set_trace()       # avec autocomplete

Puis lancer Odoo en foreground (PAS -d en daemon) :

docker compose run --rm --service-ports odoo odoo

7. Ressources externes — la liste minimaliste

🎓 Vous êtes prêt

Prochaine étape : faire les 3 exercices pratiques en autonomie, puis lire le code d'un module Ezway réel.

📋 Quiz de validation

Module 4 — Pratique 🏋️ Exercices