Ruby on Rails: How to Get Error Messages from a Child Resource Displayed

Ruby on Rails: how to get error messages from a child resource displayed?

Add a validation block in the School model to merge the errors:

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 will contain the correct errors:

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

Note:

You don't need a separate method for adding a new student to school, use the following syntax:

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

Update for Rails 3.0+

errors.add_to_base has been dropped from Rails 3.0 and above and should be replaced with:

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

How do i display devise sign_up page error messages?

The solution for me:

I just changed one line of devise registration create action.

This is the creat method of devise registration controller:

  def create
build_resource(sign_up_params)

resource.save
yield resource if block_given?
if resource.persisted?
if resource.active_for_authentication?
set_flash_message! :notice, :signed_up
sign_up(resource_name, resource)
respond_with resource, location: after_sign_up_path_for(resource)
else
set_flash_message! :notice, :"signed_up_but_#{resource.inactive_message}"
expire_data_after_sign_in!
respond_with resource, location: after_inactive_sign_up_path_for(resource)
end
else
clean_up_passwords resource
set_minimum_password_length
respond_with resource
end
end

I changed the last line respond_with resource to be render :new, status: 422

And this is the create action with the modification that I have made.

  def create
build_resource(sign_up_params)

resource.save
yield resource if block_given?
if resource.persisted?
if resource.active_for_authentication?
set_flash_message! :notice, :signed_up
sign_up(resource_name, resource)
respond_with resource, location: after_sign_up_path_for(resource)
else
set_flash_message! :notice, :"signed_up_but_#{resource.inactive_message}"
expire_data_after_sign_in!
respond_with resource, location: after_inactive_sign_up_path_for(resource)
end
else
clean_up_passwords resource
set_minimum_password_length
render :new, status: 422
end
end

Error validations from child model Ruby on Rails

Please try

def create
@film = Film.find(params[:film_id])
@review = @film.reviews.new(review_params)
if @review.save
redirect_to film_path(@film)
else
render "films/#{@film.id}"
end
end

How to show nested form validation errors after the validation errors for the parent model?

Update 1:

I discovered that the answer by februaryInk was very close to correct if you don't need to worry about i18n and translation of your application text. If you put the has_many :child_model underneath all your validations, the validations will appear in the correct order. However, full_messages doesn't appear to translate model or attribute names using the locale files, so if you require the error messages to be translated (which I do), my answer still seems like a decent solution.

Update 2:

Just realized after posting the first update that I could simplify my code that generates the messages list a lot by removing the part that does the ordering using the discovery in update 1, and just keep the part that does the translation. So here is my new solution, which is a combination of my update 1 and my original solution. All other information about the config/locales/xx.yml and config/application.rb files is still the same for this updated solution as it was for the original.

app/models/parent_model.rb

...

validates :name, # validations hash
validates :description, # validations hash
validates :others, # validations hash

has_many :child_models
accepts_nested_attributes_for :child_models

...

app/models/child_model.rb

...

validates :nested_1, # validations hash
validates :nested_2, # validations hash

...

app/helpers/application_helper.rb

messages = resource.errors.messages.keys.map {|value| error_message_attribute(resource, value) + I18n.t('space') + resource.errors.messages[value].first}.map { |msg| content_tag(:li, msg) }.join

private
def error_message_attribute(resource, symbol)
if symbol.to_s.split(".").length > 1
model_name, attribute_name = symbol.to_s.split(".")
model_class = model_name.singularize.camelize.constantize
model_class.model_name.human + I18n.t('space') + model_class.human_attribute_name(attribute_name).downcase
else
resource.class.human_attribute_name(symbol)
end
end

End of Update

I made a few changes to my error_messages function in application_helper.rb and now have everything working the way I wanted: main form validation errors are on the top, nested form validation errors are under those, the order of the errors does not change except moving the nested form errors under the main form errors.

My solution was to change the messages = line in error_messages as shown below and to add a private helper method. (This should probably be broken down into parts to make it easier to read and understand, but I built it up in the console to get what I wanted and just pasted it directly from there).

app/helpers/application_helper.rb

messages = Hash[resource.errors.messages.keys.map.with_index(1) { |attribute, index| [attribute, [index, attribute.match(/\./) ? 1 : 0]] }].sort_by {|attribute, data| [data[1], data[0]]}.collect { |attributes| attributes[0]}.map {|value| error_message_attribute_name(resource, value) + I18n.t('space') + resource.errors.messages[value].first}.map { |msg| content_tag(:li, msg) }.join

private
def error_message_attribute_name(resource, symbol)
if symbol.to_s.split(".").length > 1
model_name, attribute_name = symbol.to_s.split(".")
model_class = model_name.singularize.camelize.constantize
model_class.model_name.human + I18n.t('space') + model_class.human_attribute_name(attribute_name).downcase
else
resource.class.human_attribute_name(symbol)
end
end

This solution should also work for other other locales, since I used I18n to get all the names. You will have to add the following also:

config/locales/en.yml

en:
space: " "

This is so the model and attribute names will be handled correctly in languages that either have or don't have spaces between words (the first locale I need to support is Chinese, which doesn't have spaces between the words). If you did need to support Chinese, for example, you would use this:

config/locales/zh.yml

zh:
space: ""

If you don't have to support this case, all instances of I18n.t('space') can be replaced with " ". The model and attribute names can also be translated as, but again if you don't need to support locales beyond English you don't need to do anything (although you can use the en.yml file to change the names of the model or attributes that are displayed).

As an example using en.yml to change the names displayed using the common Authors/Books example:

config/locales/en.yml

en:
activerecord:
models:
author: "writer"
book: "manuscript"
attributes:
author:
name: "non de plume"
book:
name: "title"
published: "year"

In this example the default, without the above additions to en.yml, would be:

  • Name can't be blank.
  • Book name can't be blank.
  • Book published can't be blank.

But with the above additions to en.yml it would be:

  • Nom de plume can't be blank.
  • Manuscript title can't be blank.
  • Manuscript year can't be blank.

And of course, if you have a zh.yml file with the appropriate translations, whatever you have in those would show up instead.

If you do need to support multiple locales, don't forget to add the following to config/application.rb (this part was only tested superficially, and may need some additional configuration):

config/application.rb

config.i18n.available_locales = [:zh, :en]
config.i18n.default_locale = :en

How to populate parent object in nested attribute

You should iterate through the children objects in the parent's validation method and add the errors there instead.

Please see similar question: Ruby on Rails: how to get error messages from a child resource displayed?

How can I access the validation errors for my nested attributes?

Photo model:

  Class Photo < ActiveRecord::Base
belongs_to :article
validates :author, presence: true
end

Article model:

  class Article < ActiveRecord::Base
has_many :photos
accepts_nested_attributes_for :photos
validates_presence_of :author
end


Related Topics



Leave a reply



Submit