• Contenu
  • Bas de page
logo ouidoulogo ouidoulogo ouidoulogo ouidou
  • Qui sommes-nous ?
  • Offres
    • 💻 Applications métier
    • 🤝 Collaboration des équipes
    • 🛡️ Sécurisation et optimisation du système d’information
    • 🔗 Transformation numérique
  • Expertises
    • 🖥️ Développement logiciel
    • ♾️ DevSecOps
    • ⚙️ Intégration de logiciels et négoce de licences
      • Atlassian : Jira, Confluence, Bitbucket…
      • Plateforme monday.com
      • GitLab
      • SonarQube
    • 📚​ Logiciel de CRM et de gestion
    • 🎨 UX/UI design
    • 🌐 Accessibilité Numérique
    • 🗂️​ Démarches simplifiées
    • 📝 Formations Atlassian
    • 🤖 L’IA au service de vos projets numériques
  • Références
  • Carrières
    • 🧐 Pourquoi rejoindre Ouidou ?
    • ✍🏻 Nous rejoindre
    • 👨‍💻 Rencontrer nos collaborateurs
    • 🚀 Grandir chez Ouidou
  • RSE
  • Ressources
    • 🗞️ Actualités
    • 🔍 Articles techniques
    • 📖 Livres blancs
    • 🎙️ Interviews Clients
Nous contacter
✕
Git en entreprise
Git en entreprise
19 octobre 2021
Introduction to Project Lombok
Introduction to Project Lombok
26 octobre 2021
Ressources > Articles techniques > Indexer les erreurs sur les sous-objets

Indexer les erreurs sur les sous-objets

Article écrit par Hugo Fabre

Lors du développement d’un projet Rails en mode API uniquement, avec un ou plusieurs clients, il y a quelque chose qui n’est pas forcément facile à gérer, ce sont les erreurs. Aujourd’hui on va se pencher sur un type d’erreur assez spécifique, c’est lorsque vous sauvegardez un objet avec des sous-objets qui peuvent eux aussi être invalides. Prenons un exemple :

# models/order.rb class Order < ApplicationRecord   has_many :order_lines end  # models/order_line.rb class OrderLine < ApplicationRecord   belongs_to :order    validates :quantity, numericality: { greater_than: 0 }   validates :product, presence: true end 

C’est tout ce dont nous avons besoin en termes de code, le reste peut se faire dans une console Rails. Commençons par une situation où tout se passe bien :

order_lines = [OrderLine.new(quantity: 1, product: "controller"), OrderLine.new(quantity: 3, product: "mouse")] # => [#<OrderLine id: nil, product: "controller", quantity: 1, order_id: nil, created_at: nil, updated_at: nil>, #<OrderLine id: nil, product: "mouse", quantity: 3, order_id: nil, created_at: nil, updated_at: nil>]  o = Order.new # => #<Order id: nil, created_at: nil, updated_at: nil>  o.order_lines = order_lines # => [#<OrderLine id: nil, product: "controller", quantity: 1, order_id: nil, created_at: nil, updated_at: nil>, #<OrderLine id: nil, product: "mouse", quantity: 3, order_id: nil, created_at: nil, updated_at: nil>]  o.save # => true  o.errors # => #<ActiveModel::Errors:0x00007f97a15cea78 @base=#<Order id: 1, created_at: "2021-09-10 13:00:54.310420000 +0000", updated_at: "2021-09-10 13:00:54.310420000 +0000">, @errors=[]> 

C’est normal, pas d’erreur à déclarer ici. Passons à un cas en erreur

order_lines = [OrderLine.new(quantity: -2, product: "controller"), OrderLine.new(quantity: 3, product: "mouse")] # => [#<OrderLine id: nil, product: "controller", quantity: -2, order_id: nil, created_at: nil, updated_at: nil>, #<OrderLine id: nil, product: "mouse", quantity: 3, order_id: nil, created_at: nil, updated_at: nil>]  o = Order.new # => #<Order id: nil, created_at: nil, updated_at: nil>  o.order_lines = order_lines # => [#<OrderLine id: nil, product: "controller", quantity: -2, order_id: nil, created_at: nil, updated_at: nil>, #<OrderLine id: nil, product: "mouse", quantity: 3, order_id: nil, created_at: nil, updated_at: nil>]  o.save # => false  o.errors # => #<ActiveModel::Errors:0x00007f97a1d04ab8 @base=#<Order id: nil, created_at: nil, updated_at: nil>, @errors=[#<ActiveModel::Error attribute=order_lines, type=invalid, options={}>]>  o.errors.messages # => {:order_lines=>["is invalid"]} 

Pour expliciter un peu les erreurs des sous-modèles on peut passer par l’utilisation de accepts_nested_attributes_for

On le rajoute dans notre modèle

# models/order.rb class Order < ApplicationRecord   has_many :order_lines   accepts_nested_attributes_for :order_lines end 
o = Order.new # => #<Order id: nil, created_at: nil, updated_at: nil>  o.order_lines = order_lines # => [#<OrderLine id: nil, product: "controller", quantity: -2, order_id: nil, created_at: nil, updated_at: nil>, #<OrderLine id: nil, product: nil, quantity: -3, order_id: nil, created_at: nil, updated_at: nil>]  o.save # => false  o.errors # => #<ActiveModel::Errors:0x00007f979e71cd88 @base=#<Order id: nil, created_at: nil, updated_at: nil>, @errors=[#<ActiveModel::NestedError attribute=order_lines.quantity, type=greater_than, options={:value=>-2, :count=>0}>, #<ActiveModel::NestedError attribute=order_lines.quantity, type=greater_than, options={:value=>-3, :count=>0}>, #<ActiveModel::NestedError attribute=order_lines.product, type=blank, options={}>]>  o.errors.messages # => {:"order_lines.quantity"=>["must be greater than 0", "must be greater than 0"], :"order_lines.product"=>["can't be blank"]} 

C’est déjà mieux, mais comment savoir quelle erreur est assignée à quel sous-objet ? Et bien depuis sa version 5 Rails fourni une option sur has_many : index_errors, et cette option qui porte plutôt bien son nom sert à indexer les erreurs sur une relation has_many. Nous changeons donc notre modèle Order:

# models/order.rb class Order < ApplicationRecord   has_many :order_lines, index_errors: true # On note la nouvelle option   accepts_nested_attributes_for :order_lines end 
order_lines = [OrderLine.new(quantity: -2, product: "controller"), OrderLine.new(quantity: -3, product: nil)] # => [#<OrderLine id: nil, product: "controller", quantity: -2, order_id: nil, created_at: nil, updated_at: nil>, #<OrderLine id: nil, product: nil, quantity: -3, order_id: nil, created_at: nil, updated_at: nil>]  o = Order.new # => #<Order id: nil, created_at: nil, updated_at: nil>  o.order_lines = order_lines # => [#<OrderLine id: nil, product: "controller", quantity: -2, order_id: nil, created_at: nil, updated_at: nil>, #<OrderLine id: nil, product: nil, quantity: -3, order_id: nil, created_at: nil, updated_at: nil>]  o.save # => false  o.errors # => #<ActiveModel::Errors:0x00007f97a125af40 @base=#<Order id: nil, created_at: nil, updated_at: nil>, @errors=[#<ActiveModel::NestedError attribute=order_lines[0].quantity, type=greater_than, options={:value=>-2, :count=>0}>, #<ActiveModel::NestedError attribute=order_lines[1].quantity, type=greater_than, options={:value=>-3, :count=>0}>, #<ActiveModel::NestedError attribute=order_lines[1].product, type=blank, options={}>]>  o.errors.messages # => {:"order_lines[0].quantity"=>["must be greater than 0"], :"order_lines[1].quantity"=>["must be greater than 0"], :"order_lines[1].product"=>["can't be blank"]} 

Je vous laisse avec l’article (en anglais) qui m’a fait découvrir cette option, celle-ci n’étant aujourd’hui pas documentée.

À lire aussi

Introduction pratique au Q-learning avec Gymnasium Taxi-v3
6 janvier 2026

Introduction pratique au Q-learning avec Gymnasium Taxi-v3

Lire la suite de l'article

Introduction à K6
9 décembre 2025

Introduction à K6

Lire la suite de l'article

Créer une API en Python avec FastAPI
3 décembre 2025

Créer une API en Python avec FastAPI

Lire la suite de l'article

Optimiser vos tests avec Cucumber
17 novembre 2025

Optimiser vos tests avec Cucumber

Lire la suite de l'article

Articles associés

Introduction pratique au Q-learning avec Gymnasium Taxi-v3
6 janvier 2026

Introduction pratique au Q-learning avec Gymnasium Taxi-v3


Lire la suite
Introduction à K6
9 décembre 2025

Introduction à K6


Lire la suite
Créer une API en Python avec FastAPI
3 décembre 2025

Créer une API en Python avec FastAPI


Lire la suite

À propos

  • Qui sommes-nous ?
  • Références
  • RSE
  • Ressources

Offres

  • Applications métier
  • Collaboration des équipes
  • Sécurisation et optimisation du système d’information
  • Transformation numérique

Expertises

  • Développement logiciel
  • DevSecOps
  • Intégration de logiciels et négoce de licences
  • Logiciel de CRM et de gestion
  • UX/UI design
  • Accessibilité Numérique
  • Démarches simplifiées
  • Formations Atlassian

Carrières

  • Pourquoi rejoindre Ouidou ?
  • Nous rejoindre
  • Rencontrer nos collaborateurs
  • Grandir chez Ouidou
logo ouidou

SIEGE SOCIAL
70-74 boulevard Garibaldi, 75015 Paris

Ouidou Nord
165 Avenue de Bretagne, 59000 Lille

Ouidou Rhône-Alpes
4 place Amédée Bonnet, 69002 Lyon

Ouidou Grand-Ouest
2 rue Crucy, 44000 Nantes

Ouidou Grand-Est
7 cour des Cigarières, 67000 Strasbourg

  • Linkedin Ouidou
  • GitHub Ouidou
  • Youtube Ouidou
© 2024 Ouidou | Tous droits réservés | Plan du site | Mentions légales | Déclaration d'accessibilité
    Nous contacter