J’ai un modèle de rails qui comporte 7 atsortingbuts numériques remplis par l’utilisateur via un formulaire.
Je dois valider la présence de chacun de ces atsortingbuts, ce qui est évidemment facile à utiliser
validates :atsortingbute1, :presence => true validates :atsortingbute2, :presence => true # and so on through the atsortingbutes
Cependant, je dois également exécuter un validateur personnalisé qui prend un certain nombre d’atsortingbuts et effectue des calculs avec eux. Si le résultat de ces calculs ne se situe pas dans une certaine plage, le modèle doit être déclaré invalide.
À lui seul, 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 “valide”. Cela signifie que si l’utilisateur laisse l’un des champs obligatoires vide, rails renvoie une erreur lorsqu’il tente d’effectuer un calcul avec un atsortingbut vide.
Alors, comment puis-je vérifier la présence de tous les atsortingbuts requirejs en premier?
Je ne suis pas sûr qu’il soit garanti dans quel ordre ces validations sont exécutées, car cela peut dépendre de la manière dont le hash des atsortingbutes
lui-même est ordonné. Vous feriez peut-être mieux de rendre votre méthode de validate
plus résistante et simplement de ne pas l’exécuter si certaines des données requirejses sont manquantes. Par exemple:
def within_required_range? return if ([ a, b, c, d ].find(&:blank?)) # ... end
Cela renflouerait si l’une des variables a
à d
est vide, ce qui inclut nil, tableaux ou chaînes vides, etc.
Une alternative aux situations légèrement plus complexes consisterait à créer une méthode d’assistance qui exécute d’abord les validations pour les atsortingbuts dépendants. Alors vous pouvez faire votre: calculs_ok? validation effectuée conditionnellement.
validates :atsortingbute1, :presence => true validates :atsortingbute2, :presence => true ... validates :atsortingbute7, :presence => true validate :calculations_ok?, :unless => Proc.new { |a| a.dependent_atsortingbutes_valid? } def dependent_atsortingbutes_valid? [:atsortingbute1, ..., :atsortingbute7].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
J’ai dû créer quelque chose comme ça pour un projet parce que les validations sur les atsortingbuts dépendants étaient assez complexes. Mon équivalent de: calculs_ok? jetterait une exception si les atsortingbuts dépendants ne sont pas validés correctement.
Avantages:
Mises en garde:
Découvrez http://railscasts.com/episodes/211-validations-in-rails-3
Après avoir implémenté un validateur personnalisé, vous ferez simplement
validates :atsortingbute1, :calculations_ok => true
Ceci devrait régler votre problème.
La solution James H a le plus de sens pour moi. Une chose supplémentaire à prendre en compte est que si vous avez des conditions sur les validations dépendantes, elles doivent aussi être vérifiées dans l’ordre pour la valeur invalid_atsortingbutes_valid? appel au travail
c’est à dire.
validates :atsortingbute1, presence: true validates :atsortingbute1, uniqueness: true, if: :atsortingbute1? validates :atsortingbute1, numericality: true, unless: Proc.new {|r| r.atsortingbute1.index("@") } validates :atsortingbute2, presence: true ... validates :atsortingbute7, presence: true validate :calculations_ok?, unless: Proc.new { |a| a.dependent_atsortingbutes_valid? } def dependent_atsortingbutes_valid? [:atsortingbute1, ..., :atsortingbute7].each do |field| self.class.validators_on(field).each do |v| # Surely there is a better way with rails? existing_error = v.atsortingbutes.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