ruby on rails - validations - Controlar el orden de validaciones de rieles
rails validates if (4)
Tengo un modelo de rieles que tiene 7 atributos numéricos completados por el usuario a través de un formulario.
Necesito validar la presencia de cada uno de estos atributos, que obviamente es fácil de usar
validates :attribute1, :presence => true
validates :attribute2, :presence => true
# and so on through the attributes
Sin embargo, también necesito ejecutar un validador personalizado que tome varios atributos y haga algunos cálculos con ellos. Si el resultado de estos cálculos no está dentro de un cierto rango, entonces el modelo debe declararse no válido.
Por sí mismo, esto también es fácil
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
Sin embargo, el problema es que el método "validar" siempre se ejecuta antes de que el método "valide". Esto significa que si el usuario deja en blanco uno de los campos obligatorios, Rails arroja un error cuando intenta hacer un cálculo con un atributo en blanco.
Entonces, ¿cómo puedo verificar primero la presencia de todos los atributos requeridos?
Consulte http://railscasts.com/episodes/211-validations-in-rails-3
Después de implementar un validador personalizado, simplemente lo hará
validates :attribute1, :calculations_ok => true
Eso debería resolver tu problema.
La solución James H tiene más sentido para mí. Una cosa más a tener en cuenta, sin embargo, es que si tiene condiciones en las validaciones dependientes, también deben verificarse para el atributo dependiente_valido. llamar al trabajo.
es decir.
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
No estoy seguro de que esté garantizado en qué orden se ejecutan estas validaciones, ya que podría depender de cómo termine ordenado el propio hash de attributes
. Tal vez sea mejor que su método de validate
más resistente y simplemente no se ejecute si falta alguno de los datos requeridos. Por ejemplo:
def within_required_range?
return if ([ a, b, c, d ].find(&:blank?))
# ...
end
Esto se rescatará si cualquiera de las variables a
hasta d
está en blanco, lo que incluye nil, matrices vacías o cadenas, y así sucesivamente.
Una alternativa para situaciones ligeramente más complejas sería crear un método auxiliar que ejecute primero las validaciones para los atributos dependientes. Entonces puedes hacer tu: calculations_ok? la validación se ejecuta condicionalmente.
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
Tuve que crear algo como esto para un proyecto porque las validaciones en los atributos dependientes eran bastante complejas. Mi equivalente de: calculations_ok? lanzaría una excepción si los atributos dependientes no validarían correctamente.
Ventajas:
- relativamente seco, especialmente si sus validaciones son complejas
- asegura que su matriz de errores informe la correcta validación fallida en lugar de la macrovalidación
- incluye automáticamente cualquier validación adicional en los atributos dependientes que agrega más adelante
Advertencias:
- potencialmente ejecuta todas las validaciones dos veces
- es posible que no desee que todas las validaciones se ejecuten en los atributos dependientes