July 7, 2016
This blog is part of our Rails 5 series.
We use accepts_nested_attributes_for
when we want a single form to cater to
multiple models. By using this we can easily provide attributes for associated
models.
In Rails 4.x, if a validation fails for one or more of the associated models, then it is not possible to figure out from error message, which of the associated model object is the error related to.
class Product < ApplicationRecord
has_many :variants
accepts_nested_attributes_for :variants
end
class Variant < ApplicationRecord
validates :display_name, :price, presence: true
end
> > product = Product.new(name: 'Table')
> > variant1 = Variant.new(price: 10)
> > variant2 = Variant.new(display_name: 'Brown')
> > product.variants = [variant1, variant2]
> > product.save
> > => false
> > product.error.messages
> > => {:"variants.display_name"=>["can't be blank"], :"variants.price"=>["can't be blank"]}
In the example above we can see that if this error message is sent as JSON API, we cannot find out which variant save failed because of which attribute.
This works well when we render forms using Active Record models, as errors are available on individual instances. But, the issue arises with an API call, where we don't have access to these instances.
In Rails 5, we can add an index to errors on nested models.
We can add the option index_errors: true
to has_many
association to enable
this behavior on individual association.
class Product < ApplicationRecord
has_many :variants, index_errors: true
accepts_nested_attributes_for :variants
end
class Variant < ApplicationRecord
validates :display_name, :price, presence: true
end
> > product = Product.new(name: 'Table')
> > variant1 = Variant.new(price: 10)
> > variant2 = Variant.new(display_name: 'Brown')
> > product.variants = [variant1, variant2]
> > product.save
> > => false
> > product.error.messages
> > => {:"variants[0].display_name"=>["can't be blank"], :"variants[1].price"=>["can't be blank"]}
In order to make this change global, we can set configuration
config.active_record.index_nested_attribute_errors = true
which is false
by
default.
config.active_record.index_nested_attribute_errors = true
class Product < ApplicationRecord
has_many :variants
accepts_nested_attributes_for :variants
end
class Variant < ApplicationRecord
validates :display_name, :price, presence: true
end
This will work exactly same as an example with
has_many :variants, index_errors: true
in Product
.
If this blog was helpful, check out our full blog archive.