ruby on rails - validations - ¿Hay alguna forma de validar un atributo específico en un ActiveRecord sin crear primero un objeto?
validate form rails (8)
Por ejemplo, si tengo un modelo de usuario y necesito validar solo el inicio de sesión (lo que puede suceder al validar un formulario a través de ajax), sería genial si utilizo las mismas validaciones de modelo definidas en el modelo de usuario sin crear una instancia de usuario. .
Entonces en el controlador podría escribir código como
User.valid_attribute?(:login, "login value")
¿Hay alguna forma de que pueda hacer esto?
Dado que las validaciones operan en instancias (y utilizan el atributo de errores de una instancia como un contenedor para mensajes de error), no puede usarlos sin tener el objeto instanciado. Habiendo dicho eso, puedes ocultar este comportamiento necesario en un método de clase:
class User < ActiveRecord::Base
def self.valid_attribute?(attr, value)
mock = self.new(attr => value)
unless mock.valid?
return mock.errors.has_key?(attr)
end
true
end
end
Ahora, puedes llamar
User.valid_attribute?(:login, "login value")
tal como lo pretendiste
(Idealmente, incluirías ese método de clase directamente en ActiveRecord :: Base para que esté disponible para todos los modelos).
He ido con la solución de clase personalizada, pero solo quería asegurarme de que no había una mejor manera
class ModelValidator
def self.validate_atrribute(klass, attribute, value)
obj = Klass.new
obj.send("#{attribute}=", value)
obj.valid?
errors = obj.errors.on(attribute).to_a
return (errors.length > 0), errors
end
end
y puedo usarlo como
valid, errors = ModelValidator.validate_attribute (Usuario, "inicio de sesión", "humanzz")
Para usar sus rutinas de validación estándar:
User.new(:login => ''login_value'').valid?
Si eso no funciona para usted, cree un método de clase personalizado para esto:
class User < ActiveRecord::Base
validate do |user|
user.errors.add(''existing'') unless User.valid_login?(user.login)
end
def self.valid_login?(login)
# your validation here
!User.exist?(:login=> login)
end
end
Una implementación del método ''valid_attribute'' que está sugiriendo:
class ActiveRecord:Base
def self.valid_attribute?(attribute, value)
instance = new
instance[attribute] = value
instance.valid?
list_of_errors = instance.errors.instance_variable_get(''@errors'')[attribute]
list_of_errors && list_of_errors.size == 0
end
end
class User < ActiveRecord::Base
validates_each :login do |record, attr, value|
record.errors.add attr, ''error message here'' unless User.valid_login?(value)
end
def self.valid_login?(login)
# do validation
end
end
Simplemente llame a User.valid_login? (Login) para ver si el inicio de sesión es válido
Qué tal si:
User.columns_hash.has_key? (''Login'')
Gracias Milan por tu sugerencia. Inspirado por él, creé un módulo simple que se puede usar para agregar esta funcionalidad a cualquier clase. Tenga en cuenta que la sugerencia original de Milans tiene un error lógico como línea:
return mock.errors.has_key?(attr)
debería ser claramente:
return (not mock.errors.has_key?(attr))
He probado mi solución y debería funcionar, pero ofc no doy ninguna garantía. Y aquí está mi gloriosa solución. Básicamente un 2-liner si quitas las cosas del módulo. Acepta nombres de métodos como aguijones o símbolos.
module SingleAttributeValidation
def self.included(klass)
klass.extend(ClassMethods)
end
module ClassMethods
def valid_attribute?(attr, value)
mock = self.new(attr => value)
(not mock.valid?) && (not mock.errors.has_key?(attr.class == Symbol ? attr : attr.to_sym))
end
end
end
Me costó un montón conseguir que esto funcione en Rails 3.1. Esto finalmente funcionó. (No estoy seguro de si es la mejor manera de hacerlo, soy un poco nuevo). El problema que estaba teniendo era que ese valor se estaba configurando para que escriba ActiveSupport :: SafeBuffer, y estaba fallando la validación.
def self.valid_attribute?(attr, value)
mock = User.new(attr => "#{value}") # Rails3 SafeBuffer messes up validation
unless mock.valid?
return (not mock.errors.messages.has_key?(attr))
end
return true
end