• Contenu
  • Bas de page
blog-ouidou_logoblog-ouidou_logoblog-ouidou_logoblog-ouidou_logo
  • Actualités Ouidou
  • Articles Techniques
  • Livres blancs
  • Interviews Clients
  • Interviews collaborateurs
Découvrir Ouidou
✕
No results See all results
Git en entreprise

Git en entreprise

19 octobre 2021
Introduction to Project Lombok

Introduction to Project Lombok

26 octobre 2021
Ressources > 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

Diagnostiquer, comprendre et optimiser les performances d’une application React
4 mai 2026

Diagnostiquer, comprendre et optimiser les performances d’une application React

Lire la suite de l'article

Ouicommit Paris – IA : de l’expérimentation à l’industrialisation
15 avril 2026

Ouicommit Paris – IA : de l’expérimentation à l’industrialisation

Lire la suite de l'article

Ouidou et Scaleway : un partenariat stratégique pour accélérer le cloud souverain en Europe
8 avril 2026

Ouidou et Scaleway : un partenariat stratégique pour accélérer le cloud souverain en Europe

Lire la suite de l'article

Replay webinaire Ouidou x Sonar
2 juillet 2026

Replay webinaire Ouidou x Sonar

Lire la suite de l'article

Articles associés

Ouicommit Paris – IA : de l’expérimentation à l’industrialisation
15 avril 2026

Ouicommit Paris – IA : de l’expérimentation à l’industrialisation


Lire la suite
Ouidou et Scaleway : un partenariat stratégique pour accélérer le cloud souverain en Europe
8 avril 2026

Ouidou et Scaleway : un partenariat stratégique pour accélérer le cloud souverain en Europe


Lire la suite
Comment préparer votre Jira Cloud dès maintenant ? 
14 novembre 2025

Comment préparer votre Jira Cloud dès maintenant ? 


Lire la suite
Replay webinaire IA
5 novembre 2025

Replay webinaire IA


Lire la suite
  • Linkedin Ouidou
  • Youtube Ouidou

Blog Ouidou, le rendez-vous des équipes tech qui veulent comprendre avant de décider. Veille, retours d'expérience et décryptages pour rester à la pointe.

© 2026 Ouidou | Tous droits réservés | Plan du site | Mentions légales | Déclaration d'accessibilité
    Découvrir Ouidou
    No results See all results