J'ai un Rails modèle comportant 7 attributs numériques remplis par l'utilisateur via un formulaire.
J'ai besoin de valider la présence de chacun de ces attributs qui est évidemment facile à utiliser
validates :attribute1, :presence => true
validates :attribute2, :presence => true
# and so on through the attributes
Cependant, j'ai aussi besoin d'exécuter un validateur personnalisé qui prend un certain nombre d'attributs et fait quelques calculs avec eux. Si le résultat de ces calculs n'est pas dans une certaine plage, le modèle doit être déclaré invalide.
Sur c'est propre, c'est aussi facile
validate :calculations_ok?
def calculations_ok?
errors[:base] << "Not within required range" unless within_required_range?
end
def within_required_range?
# check the calculations and return true or false here
end
Cependant, le problème est que la méthode "VALIDATE" est toujours exécutée avant que la méthode "Validate". Cela signifie que si l'utilisateur quitte l'un des champs obligatoires en blanc, Rails jette une erreur lorsqu'il essaie de procéder à un calcul avec un attribut vierge.
Alors, comment puis-je vérifier la présence de tous les attributs requis en premier?
Je ne suis pas sûr qu'il soit garanti à quel ordre ces validations sont exécutées, car cela pourrait dépendre de la manière dont le hachage attributes
se termine par ordre. Vous risquez peut-être mieux de faire votre validate
méthode plus résiliente et tout simplement pas exécuté si certaines des données requises sont manquantes. Par exemple:
def within_required_range?
return if ([ a, b, c, d ].find(&:blank?))
# ...
end
Cela fera caution si l'une des variables a
via d
sont vides, qui comprend NIL, des tableaux vides ou des cordes, etc.
Une alternative pour des situations légèrement plus complexes serait de créer une méthode d'assistance qui exécute d'abord les validations des attributs dépendants. Ensuite, vous pouvez faire de vos: calculs_ok? la validation fonctionne conditionnellement.
validates :attribute1, :presence => true
validates :attribute2, :presence => true
...
validates :attribute7, :presence => true
validate :calculations_ok?, :unless => Proc.new { |a| a.dependent_attributes_valid? }
def dependent_attributes_valid?
[:attribute1, ..., :attribute7].each do |field|
self.class.validators_on(field).each { |v| v.validate(self) }
return false if self.errors.messages[field].present?
end
return true
end
Je devais créer quelque chose comme celui-ci pour un projet car les validations des attributs dépendants étaient assez complexes. Mon équivalent de: calculs_ok? Jeterais une exception si les attributs dépendants ne validaient pas correctement.
Avantages:
CAVEZIERS:
Vérifiez http://railcastss.com/episodes/211-validations-in-rails-
Après avoir implémenté un validateur personnalisé, vous ferez simplement
validates :attribute1, :calculations_ok => true
Cela devrait résoudre votre problème.
La solution James H offre le plus de sens pour moi. Une chose supplémentaire à considérer cependant, c'est que si vous avez des conditions sur les validations à charge, elles doivent également être vérifiées pour le dépendant_attributes_valid? appeler au travail.
c'est à dire.
validates :attribute1, presence: true
validates :attribute1, uniqueness: true, if: :attribute1?
validates :attribute1, numericality: true, unless: Proc.new {|r| r.attribute1.index("@") }
validates :attribute2, presence: true
...
validates :attribute7, presence: true
validate :calculations_ok?, unless: Proc.new { |a| a.dependent_attributes_valid? }
def dependent_attributes_valid?
[:attribute1, ..., :attribute7].each do |field|
self.class.validators_on(field).each do |v|
# Surely there is a better way with rails?
existing_error = v.attributes.select{|a| self.errors[a].present? }.present?
if_condition = v.options[:if]
validation_if_condition_passes = if_condition.blank?
validation_if_condition_passes ||= if_condition.class == Proc ? if_condition.call(self) : !!self.send(if_condition)
unless_condition = v.options[:unless]
validation_unless_condition_passes = unless_condition.blank?
validation_unless_condition_passes ||= unless_condition.class == Proc ? unless_condition.call(self) : !!self.send(unless_condition)
if !existing_error and validation_if_condition_passes and validation_unless_condition_passes
v.validate(self)
end
end
return false if self.errors.messages[field].present?
end
return true
end