ruby on rails - validations - valida asociado con el mensaje de error del modelo
rails validates if (7)
Me encontré con el mismo problema ... Quiero validar los objetos asociados, y tener el valid?
El método devuelve false, pero no agrega un mensaje (superfluo) como ''Cuenta inválida''. Solo quiero que los mensajes de validación de la cuenta aparezcan junto a los campos de la cuenta.
¡La única manera que encontré para lograr esto es anular run_validations!
, como abajo. En este ejemplo, la asociación es has_many :contacts
:
def run_validations!
valid = super
contacts.each do |contact|
valid = false unless contact.valid?(validation_context)
end
valid
end
No use valid &&= contacts.all? ...
valid &&= contacts.all? ...
porque eso evitará la validación en los contactos si la clase base ya no es válida, y detendrá la iteración si uno de los contactos falla en la validación.
Quizás es una idea generalizar esto en algún módulo de Preocupación, pero solo lo necesité una vez.
Tengo dos modelos de la siguiente manera
class User < ActiveRecord::Base
validates_associated :account
end
class Account < ActiveRecord::Base
belongs_to :user
#----------------------------------Validations--Start-------------------------
validates_length_of :unique_url, :within => 2..30 ,:message => "Should be atleast 3 characters long!"
validates_uniqueness_of :unique_url ,:message => "Already Taken"
validates_format_of :unique_url,:with => /^([a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])$/ , :message => " Cannot contain special charaters"
#----------------------------------Validations--End---------------------------
end
Ahora cuando asocio una cuenta a un usuario dice
"La cuenta no es válida"
En su lugar, quiero obtener el mensaje de error directamente de ese modelo. así que debería decir
"Should be atleast 3 characters long!"
o "Already Taken"
o " Cannot contain special charaters"
Hay alguna forma de hacer esto ?
No quiero dar un mensaje genérico como:
validates_associated :account , :message=>"one of the three validations failed"
Para aquellos que aún están en Rails 2, puede sobrescribir validates_associated con el siguiente código:
module ActiveRecord::Validations::ClassMethods
def validates_associated(association, options = {})
class_eval do
validates_each(association) do |record, associate_name, value|
associate = record.send(associate_name)
if associate && !associate.valid?
associate.errors.each do |key, value|
record.errors.add(key, value)
end
end
end
end
end
end
Fuente: http://pivotallabs.com/alias-method-chain-validates-associated-informative-error-message/
Para obtener los mensajes de error para la cuenta, tendría que llamar al método de errors
en esa instancia:
@user.account.errors
o
@account = @user.build_account
@account.errors
o en la vista:
<%= error_messages_for :account %>
Estoy asumiendo que es una relación has_one.
Puede escribir su propio validador personalizado, basado en el código del validador incorporado.
Al buscar el código fuente de validates_associated, vemos que usa el "AssociatedValidator". El código fuente para eso es:
module ActiveRecord
module Validations
class AssociatedValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
return if (value.is_a?(Array) ? value : [value]).collect{ |r| r.nil? || r.valid? }.all?
record.errors.add(attribute, :invalid, options.merge(:value => value))
end
end
module ClassMethods
def validates_associated(*attr_names)
validates_with AssociatedValidator, _merge_attributes(attr_names)
end
end
end
end
Así que puedes usar esto como un ejemplo para crear un validador personalizado que burbujee mensajes de error como este:
module ActiveRecord
module Validations
class AssociatedBubblingValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
(value.is_a?(Array) ? value : [value]).each do |v|
unless v.valid?
v.errors.full_messages.each do |msg|
record.errors.add(attribute, msg, options.merge(:value => value))
end
end
end
end
end
module ClassMethods
def validates_associated_bubbling(*attr_names)
validates_with AssociatedBubblingValidator, _merge_attributes(attr_names)
end
end
end
end
Puede poner este código en un inicializador, algo así como /initializers/associated_bubbling_validator.rb
.
Finalmente, lo validarías así:
class User < ActiveRecord::Base
validates_associated_bubbling :account
end
NOTA: el código anterior no se ha probado completamente, pero si no funciona del todo, es de esperar que sea suficiente para ponerlo en el camino correcto
Puede ser que puedes probar algo como se indica a continuación
validates_associated :account , :message=> lambda{|class_obj, obj| obj[:value].errors.full_messages.join(",") }
A través de obj[:value]
está accediendo al objeto Cuenta validado. Así obj [[valor] .errors le dará errores.
Si así lo necesita, lo siguiente funciona en Rails 5.2.
#app/validators/with_own_error_messages_validator.rb class WithOwnErrorMessagesValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) Array(value).each do |v| unless v.valid? v.errors.full_messages.each do |msg| record.errors.add(attribute, msg, options.merge(value: value)) end end end end end #app/models/project.rb class Project < ApplicationRecord validates :attachments, with_own_error_messages: true has_many :attachments, as: :attachable, validate: false end
Tenga en cuenta la validate: false
en la asociación has_many
. Esto es necesario ya que Rails ahora valida las asociaciones de forma predeterminada. Sin él, obtiene los mensajes de error del Attachment
más un Archivo Attachments is invalid
genérico Attachments is invalid
.
Espero que esto ayude.
Una solución vainilla sería tener una segunda representación de error para el objeto asociado:
<%= render :partial => ''shared/errors'', :locals => {:instance => @account.owner} %>
<%= render :partial => ''shared/errors'', :locals => {:instance => @account} %>