ruby-on-rails - create - rails generate controller home index
¿Mejor valida el método asociado para Rails 3? (4)
Rails 3 incluye validates_associated
que se llama automáticamente al guardar un modelo anidado. El problema con el método es que el mensaje es terrible: "Los modelos no son válidos"
Ha habido algunas publicaciones atacando este problema para Rails 2:
- http://rpheath.com/posts/412-a-better-validates-associated
- http://pivotallabs.com/users/nick/blog/articles/359-alias-method-chain-validates-associated-informative-error-message
y probablemente hay más. Sería genial tener una versión mejor como se describe en estas publicaciones que sea compatible con Rails 3. La mejora principal sería incluir por qué falla el modelo asociado.
En la relación, puede usar :autosave => true
lugar, que intentará guardar los modelos secundarios cuando guarde el padre. Esto ejecutará automáticamente las validaciones de los hijos e informarán con los mensajes de error adecuados.
Además, si agrega una validación de presencia en el hijo que el padre debe configurarse y construye los objetos secundarios a través de la asociación, ni siquiera necesita la autosave
y aparece un hermoso mensaje de error. Por ejemplo:
class Trip < ActiveRecord::Base
validates :name, :presence => true
attr_accessible :name
has_many :places, dependent: :destroy, :inverse_of => :trip
end
class Place < ActiveRecord::Base
belongs_to :trip
validates :name, :trip, presence: true
attr_accessible :name
end
Luego puedes obtener un buen mensaje de error con el siguiente escenario de uso:
> trip = Trip.new(name: "California")
=> #<Trip id: nil, name: "California">
> trip.places.build
=> #<Place id: nil, name: nil, trip_id: nil>
> trip.valid?
=> false
> trip.errors
=> #<ActiveModel::Errors:0x00000004d36518 @base=#<Trip id: nil, name: "California">, @messages={:places=>["is invalid"]}>
> trip.errors[:places]
=> ["is invalid"]
Creo que validates_associated
es una reliquia de la era anterior a la salvación automática de niños y ya no es la mejor manera de hacer las cosas. Por supuesto que no está necesariamente bien documentado. No estoy 100% seguro de que esto también se aplique a Rails 2.3, pero tengo la sensación de que sí. Estos cambios se produjeron cuando se agregó la característica de atributos anidados (que fue en algún momento en 2.x).
Este es un fragmento simplificado de código de un proyecto de capacitación que publiqué en github .
Estaba teniendo este problema, y al final utilicé la solución dada aquí por Ben Lee :
valida asociado con el mensaje de error del modelo
Ben dice:
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
La mayor parte del tiempo validates_existence_of es todo lo que necesito.
validates_associated ejecuta las validaciones especificadas en la clase del objeto asociado. Los errores en el nivel de clase principal simplemente dicen "mi hijo no es válido". Si desea los detalles, exponga los errores en el objeto secundario (a nivel de la forma del niño en la vista).