edge
Plus sur rubyonrails.org: Plus de Ruby on Rails

Validations Active Record

Ce guide vous apprend comment valider l'état des objets avant qu'ils ne soient enregistrés dans la base de données en utilisant la fonctionnalité de validations d'Active Record.

Après avoir lu ce guide, vous saurez :

1 Aperçu des validations

Voici un exemple de validation très simple :

class Person < ApplicationRecord
  validates :name, presence: true
end
irb> Person.create(name: "John Doe").valid?
=> true
irb> Person.create(name: nil).valid?
=> false

Comme vous pouvez le voir, notre validation nous indique que notre Person n'est pas valide sans un attribut name. La deuxième Person ne sera pas persistée dans la base de données.

Avant d'approfondir les détails, parlons de la place des validations dans l'ensemble de votre application.

1.1 Pourquoi utiliser des validations ?

Les validations sont utilisées pour s'assurer que seules des données valides sont enregistrées dans votre base de données. Par exemple, il peut être important pour votre application de s'assurer que chaque utilisateur fournit une adresse e-mail et une adresse postale valides. Les validations au niveau du modèle sont le meilleur moyen de garantir que seules des données valides sont enregistrées dans votre base de données. Elles sont indépendantes de la base de données, ne peuvent pas être contournées par les utilisateurs finaux et sont pratiques à tester et à maintenir. Rails fournit des helpers intégrés pour les besoins courants et vous permet également de créer vos propres méthodes de validation.

Il existe plusieurs autres façons de valider les données avant qu'elles ne soient enregistrées dans votre base de données, notamment les contraintes natives de la base de données, les validations côté client et les validations au niveau du contrôleur. Voici un résumé des avantages et des inconvénients :

  • Les contraintes de base de données et/ou les procédures stockées rendent les mécanismes de validation dépendants de la base de données et peuvent rendre les tests et la maintenance plus difficiles. Cependant, si votre base de données est utilisée par d'autres applications, il peut être judicieux d'utiliser certaines contraintes au niveau de la base de données. De plus, les validations au niveau de la base de données peuvent gérer en toute sécurité certaines choses (comme l'unicité dans les tables très utilisées) qui peuvent être difficiles à implémenter autrement.
  • Les validations côté client peuvent être utiles, mais elles sont généralement peu fiables si elles sont utilisées seules. Si elles sont implémentées en utilisant JavaScript, elles peuvent être contournées si JavaScript est désactivé dans le navigateur de l'utilisateur. Cependant, combinées à d'autres techniques, les validations côté client peuvent être un moyen pratique de fournir aux utilisateurs un retour immédiat lorsqu'ils utilisent votre site.
  • Les validations au niveau du contrôleur peuvent être tentantes à utiliser, mais elles deviennent souvent lourdes et difficiles à tester et à maintenir. Dans la mesure du possible, il est conseillé de garder vos contrôleurs simples, car cela rendra votre application agréable à utiliser à long terme.

Utilisez-les dans certains cas spécifiques. L'équipe de Rails estime que les validations au niveau du modèle sont les plus appropriées dans la plupart des cas.

1.2 Quand les validations se produisent-elles ?

Il existe deux types d'objets Active Record : ceux qui correspondent à une ligne dans votre base de données et ceux qui n'y correspondent pas. Lorsque vous créez un nouvel objet, par exemple en utilisant la méthode new, cet objet n'appartient pas encore à la base de données. Une fois que vous appelez save sur cet objet, il sera enregistré dans la table de base de données appropriée. Active Record utilise la méthode d'instance new_record? pour déterminer si un objet est déjà dans la base de données ou non. Considérons la classe Active Record suivante :

class Person < ApplicationRecord
end

Nous pouvons voir comment cela fonctionne en examinant la sortie de bin/rails console :

irb> p = Person.new(name: "John Doe")
=> #<Person id: nil, name: "John Doe", created_at: nil, updated_at: nil>

irb> p.new_record?
=> true

irb> p.save
=> true

irb> p.new_record?
=> false

La création et l'enregistrement d'un nouvel enregistrement enverront une opération SQL INSERT à la base de données. La mise à jour d'un enregistrement existant enverra une opération SQL UPDATE. Les validations sont généralement exécutées avant l'envoi de ces commandes à la base de données. Si une validation échoue, l'objet sera marqué comme invalide et Active Record n'effectuera pas l'opération INSERT ou UPDATE. Cela évite de stocker un objet invalide dans la base de données. Vous pouvez choisir d'exécuter des validations spécifiques lorsqu'un objet est créé, enregistré ou mis à jour.

ATTENTION : Il existe de nombreuses façons de modifier l'état d'un objet dans la base de données. Certaines méthodes déclencheront des validations, mais d'autres non. Cela signifie qu'il est possible d'enregistrer un objet dans la base de données dans un état invalide si vous n'êtes pas prudent. Les méthodes suivantes déclenchent des validations et ne sauvegardent l'objet dans la base de données que si l'objet est valide :

  • create
  • create!
  • save
  • save!
  • update
  • update!

Les versions avec un point d'exclamation (par exemple save!) lèvent une exception si l'enregistrement est invalide. Les versions sans point d'exclamation ne le font pas : save et update renvoient false, et create renvoie l'objet.

1.3 Ignorer les validations

Les méthodes suivantes ignorent les validations et sauvegardent l'objet dans la base de données quel que soit sa validité. Elles doivent être utilisées avec prudence.

  • decrement!
  • decrement_counter
  • increment!
  • increment_counter
  • insert
  • insert!
  • insert_all
  • insert_all!
  • toggle!
  • touch
  • touch_all
  • update_all
  • update_attribute
  • update_column
  • update_columns
  • update_counters
  • upsert
  • upsert_all

Notez que save a également la possibilité d'ignorer les validations si validate: false est passé en argument. Cette technique doit être utilisée avec prudence.

  • save(validate: false)

1.4 valid? et invalid?

Avant de sauvegarder un objet Active Record, Rails exécute vos validations. Si ces validations produisent des erreurs, Rails ne sauvegarde pas l'objet.

Vous pouvez également exécuter ces validations vous-même. valid? déclenche vos validations et renvoie true s'il n'y a aucune erreur dans l'objet, et false sinon. Comme vous l'avez vu ci-dessus :

class Person < ApplicationRecord
  validates :name, presence: true
end
irb> Person.create(name: "John Doe").valid?
=> true
irb> Person.create(name: nil).valid?
=> false

Après que Active Record ait effectué les validations, les échecs peuvent être consultés via la méthode d'instance errors. Celle-ci renvoie une collection d'erreurs. Par définition, un objet est valide si cette collection est vide après l'exécution des validations.

Notez qu'un objet instancié avec new ne signalera pas d'erreurs même s'il est techniquement invalide, car les validations ne sont automatiquement exécutées que lorsque l'objet est sauvegardé, par exemple avec les méthodes create ou save.

class Person < ApplicationRecord
  validates :name, presence: true
end
irb> p = Person.new
=> #<Person id: nil, name: nil>
irb> p.errors.size
=> 0

irb> p.valid?
=> false
irb> p.errors.objects.first.full_message
=> "Name can’t be blank"

irb> p = Person.create
=> #<Person id: nil, name: nil>
irb> p.errors.objects.first.full_message
=> "Name can’t be blank"

irb> p.save
=> false

irb> p.save!
ActiveRecord::RecordInvalid: Validation failed: Name can’t be blank

irb> Person.create!
ActiveRecord::RecordInvalid: Validation failed: Name can’t be blank

invalid? est l'inverse de valid?. Elle déclenche vos validations, renvoyant true si des erreurs ont été trouvées dans l'objet, et false sinon.

1.5 errors[]

Pour vérifier si un attribut particulier d'un objet est valide ou non, vous pouvez utiliser errors[:attribut]. Elle renvoie un tableau de tous les messages d'erreur pour :attribut. Si aucun erreur n'est présente sur l'attribut spécifié, un tableau vide est renvoyé.

Cette méthode n'est utile qu'après l'exécution des validations, car elle ne fait qu'inspecter la collection d'erreurs et ne déclenche pas les validations elles-mêmes. Elle est différente de la méthode ActiveRecord::Base#invalid? expliquée ci-dessus car elle ne vérifie pas la validité de l'objet dans son ensemble. Elle vérifie uniquement s'il y a des erreurs sur un attribut individuel de l'objet.

class Person < ApplicationRecord
  validates :name, presence: true
end
irb> Person.new.errors[:name].any?
=> false
irb> Person.create.errors[:name].any?
=> true

Nous aborderons les erreurs de validation plus en détail dans la section Travailler avec les erreurs de validation.

2 Aides à la validation

Active Record propose de nombreux assistants de validation prédéfinis que vous pouvez utiliser directement dans vos définitions de classe. Ces assistants fournissent des règles de validation courantes. Chaque fois qu'une validation échoue, une erreur est ajoutée à la collection errors de l'objet, et celle-ci est associée à l'attribut en cours de validation.

Chaque assistant accepte un nombre arbitraire de noms d'attributs, de sorte qu'avec une seule ligne de code, vous pouvez ajouter le même type de validation à plusieurs attributs.

Tous acceptent les options :on et :message, qui définissent quand la validation doit être exécutée et quel message doit être ajouté à la collection errors en cas d'échec, respectivement. L'option :on prend l'une des valeurs :create ou :update. Il existe un message d'erreur par défaut pour chacun des assistants de validation. Ces messages sont utilisés lorsque l'option :message n'est pas spécifiée. Examinons chacun des assistants disponibles.

INFO : Pour voir une liste des assistants par défaut disponibles, consultez ActiveModel::Validations::HelperMethods.

2.1 acceptance

Cette méthode valide si une case à cocher sur l'interface utilisateur a été cochée lorsqu'un formulaire a été soumis. Cela est généralement utilisé lorsque l'utilisateur doit accepter les conditions d'utilisation de votre application, confirmer la lecture d'un texte ou tout autre concept similaire.

class Person < ApplicationRecord
  validates :terms_of_service, acceptance: true
end

Cette vérification est effectuée uniquement si terms_of_service n'est pas nil. Le message d'erreur par défaut pour cet assistant est "doit être accepté". Vous pouvez également passer un message personnalisé via l'option message.

class Person < ApplicationRecord
  validates :terms_of_service, acceptance: { message: 'doit être respecté' }
end

Il peut également recevoir une option :accept, qui détermine les valeurs autorisées qui seront considérées comme acceptables. Par défaut, il est défini sur ['1', true] et peut être facilement modifié.

class Person < ApplicationRecord
  validates :terms_of_service, acceptance: { accept: 'yes' }
  validates :eula, acceptance: { accept: ['TRUE', 'accepted'] }
end

Cette validation est très spécifique aux applications web et cette 'acceptance' n'a pas besoin d'être enregistrée dans votre base de données. Si vous n'avez pas de champ pour cela, l'assistant créera un attribut virtuel. Si le champ existe dans votre base de données, l'option accept doit être définie sur ou inclure true, sinon la validation ne sera pas exécutée.

2.2 confirmation

Vous devez utiliser cet assistant lorsque vous avez deux champs de texte qui doivent recevoir exactement le même contenu. Par exemple, vous voudrez peut-être confirmer une adresse e-mail ou un mot de passe. Cette validation crée un attribut virtuel dont le nom est le nom du champ qui doit être confirmé avec "_confirmation" ajouté.

class Person < ApplicationRecord
  validates :email, confirmation: true
end

Dans votre modèle de vue, vous pouvez utiliser quelque chose comme

<%= text_field :person, :email %>
<%= text_field :person, :email_confirmation %>

Cette vérification est effectuée uniquement si email_confirmation n'est pas nil. Pour exiger une confirmation, assurez-vous d'ajouter une vérification de présence pour l'attribut de confirmation (nous examinerons presence plus tard dans ce guide) :

class Person < ApplicationRecord
  validates :email, confirmation: true
  validates :email_confirmation, presence: true
end

Il existe également une option :case_sensitive que vous pouvez utiliser pour définir si la contrainte de confirmation sera sensible à la casse ou non. Cette option est par défaut à true.

class Person < ApplicationRecord
  validates :email, confirmation: { case_sensitive: false }
end

Le message d'erreur par défaut pour cet assistant est "ne correspond pas à la confirmation". Vous pouvez également passer un message personnalisé via l'option message.

Généralement, lors de l'utilisation de ce validateur, vous voudrez le combiner avec l'option :if pour ne valider le champ "_confirmation" que lorsque le champ initial a été modifié et pas à chaque fois que vous enregistrez l'enregistrement. Plus d'informations sur les validations conditionnelles plus tard.

class Person < ApplicationRecord
  validates :email, confirmation: true
  validates :email_confirmation, presence: true, if: :email_changed?
end

2.3 comparison

Cette vérification valide une comparaison entre deux valeurs comparables.

class Promotion < ApplicationRecord
  validates :end_date, comparison: { greater_than: :start_date }
end

Le message d'erreur par défaut pour cet assistant est "comparaison échouée". Vous pouvez également passer un message personnalisé via l'option message.

Ces options sont toutes prises en charge :

  • :greater_than - Spécifie que la valeur doit être supérieure à la valeur fournie. Le message d'erreur par défaut pour cette option est "doit être supérieur à %{count}".
  • :greater_than_or_equal_to - Spécifie que la valeur doit être supérieure ou égale à la valeur fournie. Le message d'erreur par défaut pour cette option est "doit être supérieur ou égal à %{count}".
  • :equal_to - Spécifie que la valeur doit être égale à la valeur fournie. Le message d'erreur par défaut pour cette option est "doit être égal à %{count}".
  • :less_than - Spécifie que la valeur doit être inférieure à la valeur fournie. Le message d'erreur par défaut pour cette option est "doit être inférieur à %{count}".
  • :less_than_or_equal_to - Spécifie que la valeur doit être inférieure ou égale à la valeur fournie. Le message d'erreur par défaut pour cette option est "doit être inférieur ou égal à %{count}".
  • :other_than - Spécifie que la valeur doit être autre que la valeur fournie. Le message d'erreur par défaut pour cette option est "doit être autre que %{count}".

Le validateur nécessite une option de comparaison. Chaque option accepte une valeur, une procédure ou un symbole. Toute classe qui inclut Comparable peut être comparée.

2.4 format

Ce helper valide les valeurs des attributs en testant s'ils correspondent à une expression régulière donnée, spécifiée à l'aide de l'option :with.

class Product < ApplicationRecord
  validates :legacy_code, format: { with: /\A[a-zA-Z]+\z/,
    message: "ne permet que les lettres" }
end

Inversement, en utilisant l'option :without, vous pouvez exiger que l'attribut spécifié ne corresponde pas à l'expression régulière.

Dans les deux cas, l'option :with ou :without fournie doit être une expression régulière ou une procédure ou une lambda qui en renvoie une.

Le message d'erreur par défaut est "n'est pas valide".

AVERTISSEMENT. utilisez \A et \z pour correspondre au début et à la fin de la chaîne, ^ et $ correspondent au début/fin d'une ligne. En raison d'une utilisation fréquente incorrecte de ^ et $, vous devez passer l'option multiline: true si vous utilisez l'un de ces deux ancres dans l'expression régulière fournie. Dans la plupart des cas, vous devriez utiliser \A et \z.

2.5 inclusion

Ce helper valide que les valeurs des attributs sont incluses dans un ensemble donné. En fait, cet ensemble peut être n'importe quel objet énumérable.

class Coffee < ApplicationRecord
  validates :size, inclusion: { in: %w(small medium large),
    message: "%{value} n'est pas une taille valide" }
end

Le helper inclusion a une option :in qui reçoit l'ensemble de valeurs qui seront acceptées. L'option :in a un alias appelé :within que vous pouvez utiliser à la même fin, si vous le souhaitez. L'exemple précédent utilise l'option :message pour montrer comment vous pouvez inclure la valeur de l'attribut. Pour toutes les options, veuillez consulter la documentation sur les messages.

Le message d'erreur par défaut pour ce helper est "n'est pas inclus dans la liste".

2.6 exclusion

L'opposé de inclusion est... exclusion !

Ce helper valide que les valeurs des attributs ne sont pas incluses dans un ensemble donné. En fait, cet ensemble peut être n'importe quel objet énumérable.

class Account < ApplicationRecord
  validates :subdomain, exclusion: { in: %w(www us ca jp),
    message: "%{value} est réservé." }
end

Le helper exclusion a une option :in qui reçoit l'ensemble de valeurs qui ne seront pas acceptées pour les attributs validés. L'option :in a un alias appelé :within que vous pouvez utiliser à la même fin, si vous le souhaitez. Cet exemple utilise l'option :message pour montrer comment vous pouvez inclure la valeur de l'attribut. Pour toutes les options de l'argument message, veuillez consulter la documentation sur les messages.

Le message d'erreur par défaut est "est réservé".

Alternativement à un énumérable traditionnel (comme un tableau), vous pouvez fournir une procédure, une lambda ou un symbole qui renvoie un énumérable. Si l'énumérable est une plage numérique, temporelle ou de date et heure, le test est effectué avec Range#cover?, sinon avec include?. Lorsque vous utilisez une procédure ou une lambda, l'instance en cours de validation est passée en argument.

2.7 length

Ce helper valide la longueur des valeurs des attributs. Il offre plusieurs options, vous pouvez donc spécifier des contraintes de longueur de différentes manières :

class Person < ApplicationRecord
  validates :name, length: { minimum: 2 }
  validates :bio, length: { maximum: 500 }
  validates :password, length: { in: 6..20 }
  validates :registration_number, length: { is: 6 }
end

Les options possibles pour les contraintes de longueur sont :

  • :minimum - L'attribut ne peut pas avoir une longueur inférieure à celle spécifiée.
  • :maximum - L'attribut ne peut pas avoir une longueur supérieure à celle spécifiée.
  • :in (ou :within) - La longueur de l'attribut doit être incluse dans un intervalle donné. La valeur pour cette option doit être une plage.
  • :is - La longueur de l'attribut doit être égale à la valeur donnée.

Les messages d'erreur par défaut dépendent du type de validation de longueur effectuée. Vous pouvez personnaliser ces messages en utilisant les options :wrong_length, :too_long et :too_short et %{count} comme espace réservé pour le nombre correspondant à la contrainte de longueur utilisée. Vous pouvez toujours utiliser l'option :message pour spécifier un message d'erreur.

class Person < ApplicationRecord
  validates :bio, length: { maximum: 1000,
    too_long: "%{count} caractères est le maximum autorisé" }
end

Notez que les messages d'erreur par défaut sont au pluriel (par exemple, "est trop court (le minimum est de %{count} caractères)"). Pour cette raison, lorsque :minimum est égal à 1, vous devez fournir un message personnalisé ou utiliser presence: true à la place. Lorsque :in ou :within ont une limite inférieure de 1, vous devez soit fournir un message personnalisé, soit appeler presence avant length. REMARQUE: Une seule option de contrainte peut être utilisée à la fois, à l'exception des options :minimum et :maximum qui peuvent être combinées.

2.8 numericality

Cette méthode de validation vérifie que vos attributs ne contiennent que des valeurs numériques. Par défaut, elle accepte un signe optionnel suivi d'un nombre entier ou décimal.

Pour spécifier que seuls les nombres entiers sont autorisés, définissez :only_integer sur true. La méthode utilisera ensuite l'expression régulière suivante pour valider la valeur de l'attribut.

/\A[+-]?\d+\z/

Sinon, elle essaiera de convertir la valeur en un nombre en utilisant Float. Les Float sont convertis en BigDecimal en utilisant la précision de la colonne ou un maximum de 15 chiffres.

class Player < ApplicationRecord
  validates :points, numericality: true
  validates :games_played, numericality: { only_integer: true }
end

Le message d'erreur par défaut pour :only_integer est "doit être un entier".

En plus de :only_integer, cette méthode accepte également l'option :only_numeric, qui spécifie que la valeur doit être une instance de Numeric et essaie de la convertir si elle est une String.

REMARQUE: Par défaut, numericality n'accepte pas les valeurs nil. Vous pouvez utiliser l'option allow_nil: true pour les autoriser. Notez que pour les colonnes de type Integer et Float, les chaînes vides sont converties en nil.

Le message d'erreur par défaut lorsque aucune option n'est spécifiée est "n'est pas un nombre".

Il existe également de nombreuses options qui peuvent être utilisées pour ajouter des contraintes aux valeurs acceptables :

  • :greater_than - Spécifie que la valeur doit être supérieure à la valeur fournie. Le message d'erreur par défaut pour cette option est "doit être supérieur à %{count}".
  • :greater_than_or_equal_to - Spécifie que la valeur doit être supérieure ou égale à la valeur fournie. Le message d'erreur par défaut pour cette option est "doit être supérieur ou égal à %{count}".
  • :equal_to - Spécifie que la valeur doit être égale à la valeur fournie. Le message d'erreur par défaut pour cette option est "doit être égal à %{count}".
  • :less_than - Spécifie que la valeur doit être inférieure à la valeur fournie. Le message d'erreur par défaut pour cette option est "doit être inférieur à %{count}".
  • :less_than_or_equal_to - Spécifie que la valeur doit être inférieure ou égale à la valeur fournie. Le message d'erreur par défaut pour cette option est "doit être inférieur ou égal à %{count}".
  • :other_than - Spécifie que la valeur doit être différente de la valeur fournie. Le message d'erreur par défaut pour cette option est "doit être différent de %{count}".
  • :in - Spécifie que la valeur doit être dans la plage fournie. Le message d'erreur par défaut pour cette option est "doit être dans %{count}".
  • :odd - Spécifie que la valeur doit être un nombre impair. Le message d'erreur par défaut pour cette option est "doit être impair".
  • :even - Spécifie que la valeur doit être un nombre pair. Le message d'erreur par défaut pour cette option est "doit être pair".

2.9 presence

Cette méthode de validation vérifie que les attributs spécifiés ne sont pas vides. Elle utilise la méthode Object#blank? pour vérifier si la valeur est nil ou une chaîne vide, c'est-à-dire une chaîne vide ou composée uniquement d'espaces.

class Person < ApplicationRecord
  validates :name, :login, :email, presence: true
end

Si vous souhaitez vous assurer qu'une association est présente, vous devez vérifier si l'objet associé lui-même est présent, et non la clé étrangère utilisée pour mapper l'association. De cette façon, il est vérifié non seulement que la clé étrangère n'est pas vide, mais aussi que l'objet référencé existe.

class Supplier < ApplicationRecord
  has_one :account
  validates :account, presence: true
end

Pour valider les enregistrements associés dont la présence est requise, vous devez spécifier l'option :inverse_of pour l'association :

class Order < ApplicationRecord
  has_many :line_items, inverse_of: :order
end

REMARQUE: Si vous souhaitez vous assurer que l'association est à la fois présente et valide, vous devez également utiliser validates_associated. Plus d'informations à ce sujet ci-dessous.

Si vous validez la présence d'un objet associé via une relation has_one ou has_many, il sera vérifié que l'objet n'est ni blank? ni marked_for_destruction?.

Étant donné que false.blank? est vrai, si vous souhaitez valider la présence d'un champ booléen, vous devez utiliser l'une des validations suivantes :

# La valeur _doit_ être true ou false
validates :boolean_field_name, inclusion: [true, false]
# La valeur _ne doit pas_ être nil, c'est-à-dire true ou false
validates :boolean_field_name, exclusion: [nil]

En utilisant l'une de ces validations, vous vous assurerez que la valeur ne sera PAS nil, ce qui entraînerait une valeur NULL dans la plupart des cas.

Le message d'erreur par défaut est "ne peut pas être vide".

2.10 absence

Cette méthode de validation vérifie que les attributs spécifiés sont absents. Elle utilise la méthode Object#present? pour vérifier si la valeur n'est ni nil ni une chaîne vide, c'est-à-dire une chaîne vide ou composée uniquement d'espaces.

class Person < ApplicationRecord
  validates :name, :login, :email, absence: true
end

Si vous souhaitez vous assurer qu'une association est absente, vous devez tester si l'objet associé lui-même est absent, et non la clé étrangère utilisée pour mapper l'association.

class LineItem < ApplicationRecord
  belongs_to :order
  validates :order, absence: true
end

Pour valider les enregistrements associés dont l'absence est requise, vous devez spécifier l'option :inverse_of pour l'association :

class Order < ApplicationRecord
  has_many :line_items, inverse_of: :order
end

REMARQUE : Si vous souhaitez vous assurer que l'association est à la fois présente et valide, vous devez également utiliser validates_associated. Plus d'informations à ce sujet ci-dessous.

Si vous validez l'absence d'un objet associé via une relation has_one ou has_many, cela vérifiera que l'objet n'est ni present? ni marked_for_destruction?.

Étant donné que false.present? est faux, si vous souhaitez valider l'absence d'un champ booléen, vous devez utiliser validates :nom_du_champ, exclusion: { in: [true, false] }.

Le message d'erreur par défaut est "doit être vide".

2.11 uniqueness

Cette méthode de validation vérifie que la valeur de l'attribut est unique juste avant que l'objet ne soit enregistré.

class Account < ApplicationRecord
  validates :email, uniqueness: true
end

La validation se fait en effectuant une requête SQL dans la table du modèle, à la recherche d'un enregistrement existant avec la même valeur dans cet attribut.

Il existe une option :scope que vous pouvez utiliser pour spécifier un ou plusieurs attributs qui sont utilisés pour limiter la vérification d'unicité :

class Holiday < ApplicationRecord
  validates :name, uniqueness: { scope: :year,
    message: "ne doit se produire qu'une fois par an" }
end

ATTENTION. Cette validation ne crée pas de contrainte d'unicité dans la base de données, il peut donc arriver que deux connexions de base de données différentes créent deux enregistrements avec la même valeur pour une colonne que vous souhaitez rendre unique. Pour éviter cela, vous devez créer un index unique sur cette colonne dans votre base de données.

Pour ajouter une contrainte d'unicité à votre base de données, utilisez l'instruction add_index dans une migration et incluez l'option unique: true.

Si vous souhaitez créer une contrainte de base de données pour empêcher d'éventuelles violations d'une validation d'unicité en utilisant l'option :scope, vous devez créer un index unique sur les deux colonnes de votre base de données. Consultez le manuel MySQL pour plus de détails sur les index de plusieurs colonnes ou le manuel PostgreSQL pour des exemples de contraintes uniques qui font référence à un groupe de colonnes.

Il existe également une option :case_sensitive que vous pouvez utiliser pour définir si la contrainte d'unicité sera sensible à la casse, insensible à la casse ou respectera la collation par défaut de la base de données. Cette option est par défaut respecte la collation par défaut de la base de données.

class Person < ApplicationRecord
  validates :name, uniqueness: { case_sensitive: false }
end

ATTENTION. Notez que certaines bases de données sont configurées pour effectuer des recherches insensibles à la casse de toute façon.

Il existe une option :conditions qui vous permet de spécifier des conditions supplémentaires sous la forme d'un fragment SQL WHERE pour limiter la recherche de la contrainte d'unicité (par exemple, conditions: -> { where(status: 'active') }).

Le message d'erreur par défaut est "a déjà été pris".

Consultez validates_uniqueness_of pour plus d'informations.

2.12 validates_associated

Vous devez utiliser cette méthode d'aide lorsque votre modèle a des associations qui doivent toujours être validées. Chaque fois que vous essayez d'enregistrer votre objet, valid? sera appelé sur chacun des objets associés.

class Library < ApplicationRecord
  has_many :books
  validates_associated :books
end

Cette validation fonctionnera avec tous les types d'associations.

ATTENTION : N'utilisez pas validates_associated des deux côtés de vos associations. Ils s'appelleraient mutuellement dans une boucle infinie.

Le message d'erreur par défaut pour validates_associated est "est invalide". Notez que chaque objet associé contiendra sa propre collection d'erreurs ; les erreurs ne remontent pas au modèle appelant.

REMARQUE : validates_associated ne peut être utilisé qu'avec des objets ActiveRecord, tout ce qui précède peut également être utilisé avec n'importe quel objet qui inclut ActiveModel::Validations.

2.13 validates_each

Cette méthode auxiliaire valide les attributs par rapport à un bloc. Elle n'a pas de fonction de validation prédéfinie. Vous devez en créer une en utilisant un bloc, et chaque attribut passé à validates_each sera testé par rapport à celui-ci.

Dans l'exemple suivant, nous rejetterons les noms et prénoms qui commencent par une minuscule.

class Person < ApplicationRecord
  validates_each :name, :surname do |record, attr, value|
    record.errors.add(attr, 'doit commencer par une majuscule') if /\A[[:lower:]]/.match?(value)
  end
end

Le bloc reçoit l'enregistrement, le nom de l'attribut et la valeur de l'attribut.

Vous pouvez faire ce que vous voulez pour vérifier les données valides dans le bloc. Si votre validation échoue, vous devez ajouter une erreur au modèle, le rendant ainsi invalide.

2.14 validates_with

Cette méthode auxiliaire passe l'enregistrement à une classe distincte pour la validation.

class GoodnessValidator < ActiveModel::Validator
  def validate(record)
    if record.first_name == "Evil"
      record.errors.add :base, "Cette personne est mauvaise"
    end
  end
end

class Person < ApplicationRecord
  validates_with GoodnessValidator
end

Il n'y a pas de message d'erreur par défaut pour validates_with. Vous devez ajouter manuellement des erreurs à la collection d'erreurs de l'enregistrement dans la classe du validateur.

REMARQUE : Les erreurs ajoutées à record.errors[:base] concernent l'état de l'enregistrement dans son ensemble.

Pour implémenter la méthode de validation, vous devez accepter un paramètre record dans la définition de la méthode, qui est l'enregistrement à valider.

Si vous souhaitez ajouter une erreur sur un attribut spécifique, passez-le en premier argument, comme record.errors.add(:first_name, "veuillez choisir un autre nom"). Nous aborderons les [erreurs de validation][] plus en détail ultérieurement.

def validate(record)
  if record.some_field != "acceptable"
    record.errors.add :some_field, "ce champ est inacceptable"
  end
end

La méthode auxiliaire validates_with prend une classe, ou une liste de classes à utiliser pour la validation.

class Person < ApplicationRecord
  validates_with MyValidator, MyOtherValidator, on: :create
end

Comme toutes les autres validations, validates_with prend les options :if, :unless et :on. Si vous passez d'autres options, elles seront envoyées à la classe du validateur en tant qu'options :

class GoodnessValidator < ActiveModel::Validator
  def validate(record)
    if options[:fields].any? { |field| record.send(field) == "Evil" }
      record.errors.add :base, "Cette personne est mauvaise"
    end
  end
end

class Person < ApplicationRecord
  validates_with GoodnessValidator, fields: [:first_name, :last_name]
end

Notez que le validateur sera initialisé une seule fois pour tout le cycle de vie de l'application, et non à chaque exécution de validation, donc faites attention à l'utilisation de variables d'instance à l'intérieur.

Si votre validateur est suffisamment complexe pour nécessiter des variables d'instance, vous pouvez facilement utiliser un simple objet Ruby :

class Person < ApplicationRecord
  validate do |person|
    GoodnessValidator.new(person).validate
  end
end

class GoodnessValidator
  def initialize(person)
    @person = person
  end

  def validate
    if some_complex_condition_involving_ivars_and_private_methods?
      @person.errors.add :base, "Cette personne est mauvaise"
    end
  end

  # ...
end

Nous aborderons les validations personnalisées plus tard.

erreurs de validation

3 Options de validation courantes

Il existe plusieurs options courantes prises en charge par les validateurs que nous venons de voir, examinons-en quelques-unes maintenant !

REMARQUE : Toutes ces options ne sont pas prises en charge par tous les validateurs, veuillez vous référer à la documentation de l'API pour ActiveModel::Validations.

En utilisant l'une des méthodes de validation que nous venons de mentionner, il existe également une liste d'options courantes partagées avec les validateurs. Nous les aborderons maintenant !

  • :allow_nil : Ignorer la validation si l'attribut est nil.
  • :allow_blank : Ignorer la validation si l'attribut est vide.
  • :message : Spécifier un message d'erreur personnalisé.
  • :on : Spécifier les contextes où cette validation est active.
  • :strict : Lever une exception lorsque la validation échoue.
  • :if et :unless : Spécifier quand la validation doit ou ne doit pas se produire.

3.1 :allow_nil

L'option :allow_nil ignore la validation lorsque la valeur à valider est nil.

class Coffee < ApplicationRecord
  validates :size, inclusion: { in: %w(small medium large),
    message: "%{value} n'est pas une taille valide" }, allow_nil: true
end
irb> Coffee.create(size: nil).valid?
=> true
irb> Coffee.create(size: "mega").valid?
=> false

Pour toutes les options de l'argument message, veuillez consulter la documentation sur les messages.

3.2 :allow_blank

L'option :allow_blank est similaire à l'option :allow_nil. Cette option permet à la validation de passer si la valeur de l'attribut est blank?, comme nil ou une chaîne vide par exemple.

class Topic < ApplicationRecord
  validates :title, length: { is: 5 }, allow_blank: true
end
irb> Topic.create(title: "").valid?
=> true
irb> Topic.create(title: nil).valid?
=> true

3.3 :message

Comme vous l'avez déjà vu, l'option :message vous permet de spécifier le message qui sera ajouté à la collection errors lorsque la validation échoue. Lorsque cette option n'est pas utilisée, Active Record utilisera le message d'erreur par défaut correspondant à chaque helper de validation.

L'option :message accepte soit une String soit un Proc comme valeur.

Une valeur String pour :message peut éventuellement contenir %{value}, %{attribute} et %{model}, qui seront remplacés dynamiquement lorsque la validation échoue. Ce remplacement est effectué à l'aide de la gem i18n, et les espaces ne sont pas autorisés dans les espaces réservés.

class Person < ApplicationRecord
  # Message codé en dur
  validates :name, presence: { message: "doit être renseigné s'il vous plaît" }

  # Message avec une valeur d'attribut dynamique. %{value} sera remplacé
  # par la valeur réelle de l'attribut. %{attribute} et %{model}
  # sont également disponibles.
  validates :age, numericality: { message: "%{value} semble incorrect" }
end

Une valeur Proc pour :message est donnée deux arguments : l'objet en cours de validation et un hash avec les paires clé-valeur :model, :attribute et :value.

class Person < ApplicationRecord
  validates :username,
    uniqueness: {
      # object = objet person en cours de validation
      # data = { model: "Person", attribute: "Username", value: <username> }
      message: ->(object, data) do
        "Hey #{object.name}, #{data[:value]} est déjà pris."
      end
    }
end

3.4 :on

L'option :on vous permet de spécifier quand la validation doit être effectuée. Le comportement par défaut de tous les helpers de validation intégrés est de s'exécuter lors de l'enregistrement (à la fois lors de la création d'un nouvel enregistrement et lors de sa mise à jour). Si vous souhaitez le modifier, vous pouvez utiliser on: :create pour exécuter la validation uniquement lors de la création d'un nouvel enregistrement ou on: :update pour exécuter la validation uniquement lors de la mise à jour d'un enregistrement.

class Person < ApplicationRecord
  # il sera possible de mettre à jour l'e-mail avec une valeur en double
  validates :email, uniqueness: true, on: :create

  # il sera possible de créer l'enregistrement avec un âge non numérique
  validates :age, numericality: true, on: :update

  # par défaut (valide à la fois lors de la création et de la mise à jour)
  validates :name, presence: true
end

Vous pouvez également utiliser on: pour définir des contextes personnalisés. Les contextes personnalisés doivent être déclenchés explicitement en passant le nom du contexte à valid?, invalid? ou save.

class Person < ApplicationRecord
  validates :email, uniqueness: true, on: :account_setup
  validates :age, numericality: true, on: :account_setup
end
irb> person = Person.new(age: 'trente-trois')
irb> person.valid?
=> true
irb> person.valid?(:account_setup)
=> false
irb> person.errors.messages
=> {:email=>["a déjà été pris"], :age=>["n'est pas un nombre"]}

person.valid?(:account_setup) exécute les deux validations sans enregistrer le modèle. person.save(context: :account_setup) valide person dans le contexte account_setup avant de l'enregistrer.

Passer un tableau de symboles est également acceptable.

class Book
  include ActiveModel::Validations

  validates :title, presence: true, on: [:update, :ensure_title]
end
irb> book = Book.new(title: nil)
irb> book.valid?
=> true
irb> book.valid?(:ensure_title)
=> false
irb> book.errors.messages
=> {:title=>["ne peut pas être vide"]}

Lorsqu'ils sont déclenchés par un contexte explicite, les validations sont exécutées pour ce contexte, ainsi que pour toutes les validations sans contexte.

class Person < ApplicationRecord
  validates :email, uniqueness: true, on: :account_setup
  validates :age, numericality: true, on: :account_setup
  validates :name, presence: true
end
irb> person = Person.new
irb> person.valid?(:account_setup)
=> false
irb> person.errors.messages
=> {:email=>["a déjà été pris"], :age=>["n'est pas un nombre"], :name=>["ne peut pas être vide"]}

Nous aborderons d'autres cas d'utilisation de on: dans le guide des callbacks.

4 Validations strictes

Vous pouvez également spécifier des validations strictes et lever une exception ActiveModel::StrictValidationFailed lorsque l'objet est invalide.

class Person < ApplicationRecord
  validates :name, presence: { strict: true }
end
irb> Person.new.valid?
ActiveModel::StrictValidationFailed: Le nom ne peut pas être vide

Il est également possible de passer une exception personnalisée à l'option :strict.

class Person < ApplicationRecord
  validates :token, presence: true, uniqueness: true, strict: TokenGenerationException
end
irb> Person.new.valid?
TokenGenerationException: Le token ne peut pas être vide

5 Validation conditionnelle

Il peut parfois être logique de valider un objet uniquement lorsque un prédicat donné est satisfait. Vous pouvez le faire en utilisant les options :if et :unless, qui peuvent prendre un symbole, un Proc ou un tableau. Vous pouvez utiliser l'option :if lorsque vous souhaitez spécifier quand la validation doit avoir lieu. Alternativement, si vous souhaitez spécifier quand la validation ne doit pas avoir lieu, vous pouvez utiliser l'option :unless.

5.1 Utilisation d'un symbole avec :if et :unless

Vous pouvez associer les options :if et :unless à un symbole correspondant au nom d'une méthode qui sera appelée juste avant la validation. C'est l'option la plus couramment utilisée.

class Order < ApplicationRecord
  validates :card_number, presence: true, if: :paid_with_card?

  def paid_with_card?
    payment_type == "card"
  end
end

5.2 Utilisation d'un Proc avec :if et :unless

Il est possible d'associer :if et :unless à un objet Proc qui sera appelé. L'utilisation d'un objet Proc vous permet d'écrire une condition en ligne au lieu d'une méthode distincte. Cette option est particulièrement adaptée aux instructions d'une seule ligne.

class Account < ApplicationRecord
  validates :password, confirmation: true,
    unless: Proc.new { |a| a.password.blank? }
end

Comme lambda est un type de Proc, il peut également être utilisé pour écrire des conditions en ligne en profitant de la syntaxe raccourcie.

validates :password, confirmation: true, unless: -> { password.blank? }

5.3 Regroupement des validations conditionnelles

Il est parfois utile de faire en sorte que plusieurs validations utilisent une même condition. Cela peut être facilement réalisé en utilisant with_options.

class User < ApplicationRecord
  with_options if: :is_admin? do |admin|
    admin.validates :password, length: { minimum: 10 }
    admin.validates :email, presence: true
  end
end

Toutes les validations à l'intérieur du bloc with_options passeront automatiquement la condition if: :is_admin?

5.4 Combinaison des conditions de validation

D'autre part, lorsque plusieurs conditions définissent si une validation doit être effectuée ou non, un Array peut être utilisé. De plus, vous pouvez appliquer à la fois :if et :unless à la même validation.

class Computer < ApplicationRecord
  validates :mouse, presence: true,
                    if: [Proc.new { |c| c.market.retail? }, :desktop?],
                    unless: Proc.new { |c| c.trackpad.present? }
end

La validation n'est exécutée que lorsque toutes les conditions :if et aucune des conditions :unless sont évaluées à true.

6 Réalisation de validations personnalisées

Lorsque les helpers de validation intégrés ne suffisent pas à vos besoins, vous pouvez écrire vos propres validateurs ou méthodes de validation selon vos préférences.

6.1 Validateurs personnalisés

Les validateurs personnalisés sont des classes qui héritent de ActiveModel::Validator. Ces classes doivent implémenter la méthode validate qui prend un enregistrement en argument et effectue la validation sur celui-ci. Le validateur personnalisé est appelé en utilisant la méthode validates_with.

class MyValidator < ActiveModel::Validator
  def validate(record)
    unless record.name.start_with? 'X'
      record.errors.add :name, "Fournissez un nom commençant par X, s'il vous plaît !"
    end
  end
end

class Person < ApplicationRecord
  validates_with MyValidator
end

La manière la plus simple d'ajouter des validateurs personnalisés pour valider des attributs individuels est d'utiliser le pratique ActiveModel::EachValidator. Dans ce cas, la classe de validateur personnalisé doit implémenter une méthode validate_each qui prend trois arguments : l'enregistrement, l'attribut à valider et la valeur de l'attribut dans l'instance passée.

class EmailValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    unless URI::MailTo::EMAIL_REGEXP.match?(value)
      record.errors.add attribute, (options[:message] || "n'est pas une adresse e-mail")
    end
  end
end

class Person < ApplicationRecord
  validates :email, presence: true, email: true
end

Comme le montre l'exemple, vous pouvez également combiner des validations standard avec vos propres validateurs personnalisés.

6.2 Méthodes personnalisées

Vous pouvez également créer des méthodes qui vérifient l'état de vos modèles et ajoutent des erreurs à la collection errors lorsqu'ils sont invalides. Vous devez ensuite enregistrer ces méthodes en utilisant la méthode de classe validate, en passant les symboles correspondant aux noms des méthodes de validation.

Vous pouvez passer plus d'un symbole pour chaque méthode de classe et les validations respectives seront exécutées dans le même ordre que celui dans lequel elles ont été enregistrées.

La méthode valid? vérifiera que la collection errors est vide, donc vos méthodes de validation personnalisées doivent y ajouter des erreurs lorsque vous souhaitez que la validation échoue :

class Invoice < ApplicationRecord
  validate :expiration_date_cannot_be_in_the_past,
    :discount_cannot_be_greater_than_total_value

  def expiration_date_cannot_be_in_the_past
    if expiration_date.present? && expiration_date < Date.today
      errors.add(:expiration_date, "ne peut pas être dans le passé")
    end
  end

  def discount_cannot_be_greater_than_total_value
    if discount > total_value
      errors.add(:discount, "ne peut pas être supérieur à la valeur totale")
    end
  end
end

Par défaut, ces validations seront exécutées à chaque appel de valid? ou de sauvegarde de l'objet. Mais il est également possible de contrôler quand exécuter ces validations personnalisées en donnant une option :on à la méthode validate, avec soit :create soit :update.

class Invoice < ApplicationRecord
  validate :active_customer, on: :create

  def active_customer
    errors.add(:customer_id, "n'est pas actif") unless customer.active?
  end
end

Voir la section ci-dessus pour plus de détails sur :on.

6.3 Liste des validateurs

Si vous souhaitez connaître tous les validateurs pour un objet donné, ne cherchez pas plus loin que validators.

Par exemple, si nous avons le modèle suivant utilisant un validateur personnalisé et un validateur intégré :

class Person < ApplicationRecord
  validates :name, presence: true, on: :create
  validates :email, format: URI::MailTo::EMAIL_REGEXP
  validates_with MyOtherValidator, strict: true
end

Nous pouvons maintenant utiliser validators sur le modèle "Person" pour lister tous les validateurs, ou même vérifier un champ spécifique en utilisant validators_on.

irb> Person.validators
#=> [#<ActiveRecord::Validations::PresenceValidator:0x10b2f2158
      @attributes=[:name], @options={:on=>:create}>,
     #<MyOtherValidatorValidator:0x10b2f17d0
      @attributes=[:name], @options={:strict=>true}>,
     #<ActiveModel::Validations::FormatValidator:0x10b2f0f10
      @attributes=[:email],
      @options={:with=>URI::MailTo::EMAIL_REGEXP}>]
     #<MyOtherValidator:0x10b2f0948 @options={:strict=>true}>]

irb> Person.validators_on(:name)
#=> [#<ActiveModel::Validations::PresenceValidator:0x10b2f2158
      @attributes=[:name], @options={on: :create}>]

7 Travailler avec les erreurs de validation

Les méthodes valid? et invalid? ne fournissent qu'un résumé de l'état de validité. Cependant, vous pouvez approfondir chaque erreur individuelle en utilisant différentes méthodes de la collection errors.

Voici une liste des méthodes les plus couramment utilisées. Veuillez vous référer à la documentation ActiveModel::Errors pour une liste de toutes les méthodes disponibles.

7.1 errors

La passerelle par laquelle vous pouvez accéder à divers détails de chaque erreur.

Cela renvoie une instance de la classe ActiveModel::Errors contenant toutes les erreurs, chaque erreur étant représentée par un objet ActiveModel::Error.

class Person < ApplicationRecord
  validates :name, presence: true, length: { minimum: 3 }
end
irb> person = Person.new
irb> person.valid?
=> false
irb> person.errors.full_messages
=> ["Le nom ne peut pas être vide", "Le nom est trop court (au minimum 3 caractères)"]

irb> person = Person.new(name: "John Doe")
irb> person.valid?
=> true
irb> person.errors.full_messages
=> []

irb> person = Person.new
irb> person.valid?
=> false
irb> person.errors.first.details
=> {:error=>:too_short, :count=>3}

7.2 errors[]

errors[] est utilisé lorsque vous souhaitez vérifier les messages d'erreur pour un attribut spécifique. Il renvoie un tableau de chaînes avec tous les messages d'erreur pour l'attribut donné, chaque chaîne avec un message d'erreur. S'il n'y a pas d'erreurs liées à l'attribut, il renvoie un tableau vide.

class Person < ApplicationRecord
  validates :name, presence: true, length: { minimum: 3 }
end
irb> person = Person.new(name: "John Doe")
irb> person.valid?
=> true
irb> person.errors[:name]
=> []

irb> person = Person.new(name: "JD")
irb> person.valid?
=> false
irb> person.errors[:name]
=> ["est trop court (au minimum 3 caractères)"]

irb> person = Person.new
irb> person.valid?
=> false
irb> person.errors[:name]
=> ["ne peut pas être vide", "est trop court (au minimum 3 caractères)"]

7.3 errors.where et objet d'erreur

Parfois, nous avons besoin de plus d'informations sur chaque erreur en plus de son message. Chaque erreur est encapsulée en tant qu'objet ActiveModel::Error, et la méthode where est le moyen le plus courant d'y accéder.

where renvoie un tableau d'objets d'erreur filtrés par différents degrés de conditions.

class Person < ApplicationRecord
  validates :name, presence: true, length: { minimum: 3 }
end

Nous pouvons filtrer uniquement l'attribut en le passant comme premier paramètre à errors.where(:attr). Le deuxième paramètre est utilisé pour filtrer le type d'erreur que nous voulons en appelant errors.where(:attr, :type).

irb> person = Person.new
irb> person.valid?
=> false

irb> person.errors.where(:name)
=> [ ... ] # toutes les erreurs pour l'attribut :name

irb> person.errors.where(:name, :too_short)
=> [ ... ] # erreurs :too_short pour l'attribut :name

Enfin, nous pouvons filtrer par toutes les options qui peuvent exister sur le type d'objet d'erreur donné.

irb> person = Person.new
irb> person.valid?
=> false

irb> person.errors.where(:name, :too_short, minimum: 3)
=> [ ... ] # toutes les erreurs de nom étant trop courtes et le minimum est de 2

Vous pouvez lire diverses informations à partir de ces objets d'erreur :

irb> error = person.errors.where(:name).last

irb> error.attribute
=> :name
irb> error.type
=> :too_short
irb> error.options[:count]
=> 3

Vous pouvez également générer le message d'erreur :

irb> error.message
=> "est trop court (au minimum 3 caractères)"
irb> error.full_message
=> "Le nom est trop court (au minimum 3 caractères)"

La méthode full_message génère un message plus convivial pour l'utilisateur, avec le nom de l'attribut en majuscule précédé. (Pour personnaliser le format utilisé par full_message, consultez le guide I18n.)

7.4 errors.add

La méthode add crée l'objet d'erreur en prenant l'attribut, le type d'erreur et un hachage d'options supplémentaires. Cela est utile lorsque vous écrivez votre propre validateur, car cela vous permet de définir des situations d'erreur très spécifiques.

class Person < ApplicationRecord
  validate do |person|
    errors.add :name, :too_plain, message: "n'est pas assez cool"
  end
end
irb> person = Person.create
irb> person.errors.where(:name).first.type
=> :too_plain
irb> person.errors.where(:name).first.full_message
=> "Le nom n'est pas assez cool"

7.5 errors[:base]

Vous pouvez ajouter des erreurs qui sont liées à l'état de l'objet dans son ensemble, au lieu d'être liées à un attribut spécifique. Pour cela, vous devez utiliser :base comme attribut lors de l'ajout d'une nouvelle erreur.

class Person < ApplicationRecord
  validate do |person|
    errors.add :base, :invalid, message: "Cette personne est invalide car ..."
  end
end
irb> person = Person.create
irb> person.errors.where(:base).first.full_message
=> "Cette personne est invalide car ..."

7.6 errors.size

La méthode size renvoie le nombre total d'erreurs pour l'objet.

class Person < ApplicationRecord
  validates :name, presence: true, length: { minimum: 3 }
end
irb> person = Person.new
irb> person.valid?
=> false
irb> person.errors.size
=> 2

irb> person = Person.new(name: "Andrea", email: "[email protected]")
irb> person.valid?
=> true
irb> person.errors.size
=> 0

7.7 errors.clear

La méthode clear est utilisée lorsque vous souhaitez intentionnellement effacer la collection errors. Bien sûr, appeler errors.clear sur un objet invalide ne le rendra pas valide : la collection errors sera maintenant vide, mais la prochaine fois que vous appellerez valid? ou toute méthode qui tente d'enregistrer cet objet dans la base de données, les validations seront à nouveau exécutées. Si l'une des validations échoue, la collection errors sera à nouveau remplie.

class Person < ApplicationRecord
  validates :name, presence: true, length: { minimum: 3 }
end
irb> person = Person.new
irb> person.valid?
=> false
irb> person.errors.empty?
=> false

irb> person.errors.clear
irb> person.errors.empty?
=> true

irb> person.save
=> false

irb> person.errors.empty?
=> false

8 Affichage des erreurs de validation dans les vues

Une fois que vous avez créé un modèle et ajouté des validations, si ce modèle est créé via un formulaire web, vous voulez probablement afficher un message d'erreur lorsque l'une des validations échoue.

Étant donné que chaque application gère ce genre de chose différemment, Rails n'inclut aucun helper de vue pour vous aider à générer ces messages directement. Cependant, grâce au grand nombre de méthodes que Rails vous donne pour interagir avec les validations en général, vous pouvez construire les vôtres. De plus, lors de la génération d'un scaffold, Rails ajoutera du code ERB dans le fichier _form.html.erb qu'il génère pour afficher la liste complète des erreurs sur ce modèle.

En supposant que nous avons un modèle qui a été enregistré dans une variable d'instance nommée @article, cela ressemble à ceci :

<% if @article.errors.any? %>
  <div id="error_explanation">
    <h2><%= pluralize(@article.errors.count, "erreur") %> ont empêché l'enregistrement de cet article :</h2>

    <ul>
      <% @article.errors.each do |error| %>
        <li><%= error.full_message %></li>
      <% end %>
    </ul>
  </div>
<% end %>

De plus, si vous utilisez les helpers de formulaire de Rails pour générer vos formulaires, lorsqu'une erreur de validation se produit sur un champ, il générera un <div> supplémentaire autour de l'entrée.

<div class="field_with_errors">
  <input id="article_title" name="article[title]" size="30" type="text" value="">
</div>

Vous pouvez ensuite styliser ce div comme vous le souhaitez. Le scaffold par défaut que Rails génère, par exemple, ajoute cette règle CSS :

.field_with_errors {
  padding: 2px;
  background-color: red;
  display: table;
}

Cela signifie que tout champ avec une erreur se retrouve avec une bordure rouge de 2 pixels.

Retour d'information

Vous êtes encouragé à contribuer à l'amélioration de la qualité de ce guide.

Veuillez contribuer si vous trouvez des fautes de frappe ou des erreurs factuelles. Pour commencer, vous pouvez lire notre contribution à la documentation section.

Vous pouvez également trouver du contenu incomplet ou des informations qui ne sont pas à jour. Veuillez ajouter toute documentation manquante pour la version principale. Assurez-vous de vérifier Edge Guides d'abord pour vérifier si les problèmes ont déjà été résolus ou non sur la branche principale. Consultez les Directives des guides Ruby on Rails pour le style et les conventions.

Si pour une raison quelconque vous repérez quelque chose à corriger mais ne pouvez pas le faire vous-même, veuillez ouvrir un problème.

Et enfin, toute discussion concernant la documentation de Ruby on Rails est la bienvenue sur le Forum officiel de Ruby on Rails.