restful rails new mvc create crear blog ruby-on-rails ruby activerecord rest

ruby-on-rails - new - ruby on rails api rest



Ruby on Rails: ¿cómo se muestran los mensajes de error de un recurso infantil? (8)

Estoy teniendo dificultades para entender cómo hacer que Rails muestre un mensaje de error explícito para un recurso hijo que está fallando la validación cuando presento una plantilla XML. Hipotéticamente, tengo las siguientes clases:

class School < ActiveRecord::Base has_many :students validates_associated :students def self.add_student(bad_email) s = Student.new(bad_email) students << s end end class Student < ActiveRecord::Base belongs_to :school validates_format_of :email, :with => /^([^@/s]+)@((?:[-a-z0-9]+/.)+[a-z]{2,})$/i, :message => "You must supply a valid email" end

Ahora, en el controlador, digamos que queremos construir una API trivial que nos permita agregar una nueva Escuela con un estudiante (de nuevo, dije, es un ejemplo terrible, pero juega su papel para el propósito de la pregunta)

class SchoolsController < ApplicationController def create @school = School.new @school.add_student(params[:bad_email]) respond_to do |format| if @school.save # some code else format.xml { render :xml => @school.errors, :status => :unprocessable_entity } end end end end

Ahora la validación está funcionando bien, las cosas se mueren porque el correo electrónico no coincide con la expresión regular establecida en el método validates_format_of en la clase Estudiante. Sin embargo, el resultado que obtengo es el siguiente:

<?xml version="1.0" encoding="UTF-8"?> <errors> <error>Students is invalid</error> </errors>

Quiero que aparezca el mensaje de error más significativo que configuré arriba con validates_format_of. Es decir, quiero que diga:

<error>You must supply a valid email</error>

¿Qué estoy haciendo mal para que no aparezca?


Actualizar Rails 5.0.1

Puede usar Active Record Autosave Association

class School < ActiveRecord::Base has_many :students, autosave: true validates_associated :students end class Student < ActiveRecord::Base belongs_to :school validates_format_of :email, :with => /^([^@/s]+)@((?:[-a-z0-9]+/.)+[a-z]{2,})$/i, :message => "You must supply a valid email" end @school = School.new @school.build_student(email: ''xyz'') @school.save @school.errors.full_messages ==> [''You must supply a valid email'']

referencia: http://api.rubyonrails.org/classes/ActiveRecord/AutosaveAssociation.html


Agregue un bloque de validación en el modelo de la School para combinar los errores:

class School < ActiveRecord::Base has_many :students validate do |school| school.students.each do |student| next if student.valid? student.errors.full_messages.each do |msg| # you can customize the error message here: errors.add_to_base("Student Error: #{msg}") end end end end

Now @school.errors contendrá los errores correctos:

format.xml { render :xml => @school.errors, :status => :unprocessable_entity }

Nota:

No necesita un método diferente para agregar un nuevo alumno a la escuela, use la siguiente sintaxis:

school.students.build(:email => email)

Actualización para Rails 3.0+

errors.add_to_base ha sido eliminado de Rails 3.0 y superior y debe ser reemplazado por:

errors[:base] << "Student Error: #{msg}"


Aquí hay un ejemplo que podría soportar algunos DRYing:

def join_model_and_association_errors!(model) klass = model.class has_manys = klass.reflect_on_all_associations(:has_many) has_ones = klass.reflect_on_all_associations(:has_one) belong_tos = klass.reflect_on_all_associations(:belongs_to) habtms = klass.reflect_on_all_associations(:has_and_belongs_to_many) collection_associations = [has_manys, habtms].flatten instance_associations = [has_ones, belong_tos].flatten (collection_associations + instance_associations).each do |association| model.errors.delete(association.name) end collection_associations.each do |association| model.send(association.name).each do |child| next if child.valid? errors = child.errors.full_messages model.errors[:base] << "#{association.class_name} Invalid: #{errors.to_sentence}" end end instance_associations.each do |association| next unless child = model.send(association.name) next if child.valid? errors = child.errors.full_messages model.errors[:base] << "#{association.class_name} Invalid: #{errors.to_sentence}" end model.errors end


Deberías usar following en el rhtml.

<%= error_messages_for :school, :student %>

Para omitir el mensaje " Estudiantes no es válido ", utilice el siguiente mensaje en student.rb

def after_validation # Skip errors that won''t be useful to the end user filtered_errors = self.errors.reject{ |err| %w{ student}.include?(err.first) } self.errors.clear filtered_errors.each { |err| self.errors.add(*err) } end

EDITADO

Sorry after_validation must be in a school.rb


Esta aún no es una API pública, pero Rails 5 stable parece tener ActiveModel::Errors#copy! para combinar errors entre dos modelos.

user = User.new(name: "foo", email: nil) other = User.new(name: nil, email:"[email protected]") user.errors.copy!(other.errors) user.full_messages #=> [ "name is blank", "email is blank" ]

Una vez más, esto aún no se ha publicado oficialmente (accidentalmente lo encuentro antes de la clase de Errors reparación de monos), y no estoy seguro de que así sea.

Eso depende de ti.


No estoy seguro de si esta es la mejor (o la correcta) respuesta ... todavía estoy aprendiendo, pero encontré que esto funciona bastante bien. No lo he probado exhaustivamente, pero parece funcionar con rails4:

validate do |school| school.errors.delete(:students) school.students.each do |student| next if student.valid? school.errors.add(:students, student.errors) end end


Tengo el mismo problema. no hay una buena respuesta hasta el momento. Así que lo resolví solo. reemplazando el mensaje de error de asociación con el mensaje de error de detalle:

crear un archivo de preocupación models/concerns/association_error_detail_concern.rb :

module AssociationErrorDetailConcern extend ActiveSupport::Concern included do after_validation :replace_association_error_message end class_methods do def association_names @association_names ||= self.reflect_on_all_associations.map(&:name) end end def replace_association_error_message self.class.association_names.each do |attr| next unless errors[attr] errors.delete(attr) Array.wrap(public_send(attr)).each do |record| record.errors.full_messages.each do |message| errors.add(attr, message) end end end end end

en tu modelo:

class School < ApplicationRecord include AssociationErrorDetailConcern has_many :students ... end

luego obtendrá you must supply a valid email error de you must supply a valid email sobre el atributo de los students del registro school . en lugar de mensaje inútil is invalid


Veo un problema en el código publicado. add_student es un método de clase de la clase School , de modo que self apuntará al objeto de clase School lugar de a un objeto instancia de la clase School . Los students << s línea students << s no agregarán los registros a la school discográfica debido a esto.

No sé si esto está causando el mensaje de error, pero creo que esto evitará que el código funcione correctamente.