Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Errors can be indexed with nested attributes #19686

Merged
merged 1 commit into from
Oct 26, 2015
Merged

Conversation

mprobber
Copy link
Contributor

@mprobber mprobber commented Apr 7, 2015

accepts_nested_attributes_for can now take index_errors: true as an
option. When this is enabled, errors for nested models will be
returned alongside an index, as opposed to just the nested model name.
This option can also be enabled globally in a configuration file.

@tsun1215
#8638

@@ -21,6 +21,9 @@
# Show backtraces for deprecated behavior for quicker cleanup.
ActiveSupport::Deprecation.debug = true

# Don't add indeces to nested attribute errors
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will change, but isn't it indices?

now be indexed if :index_errors is specified when defining the nested
attributes, or if its set in the global config.

*Michael Probber and Terence Sun*
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done!

options = self.nested_attributes_options[reflection.name]
if ActiveRecord::Base.index_nested_attribute_errors
attribute = "#{reflection.name}[#{index}].#{attribute}"
elsif index.nil? or options.nil? or not options[:index_errors]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use || and ! here to be in line with our style guide 😄

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. We'll update once we figured out how we should break the conditional up (see above).

@mprobber mprobber force-pushed the index_errors branch 5 times, most recently from 695b20b to b8cbac1 Compare April 8, 2015 22:06
`has_many` can now take `index_errors: true` as an
option.  When this is enabled, errors for nested models will be
returned alongside an index, as opposed to just the nested model name.
This option can also be enabled (or disabled) globally through
`ActiveRecord::Base.index_nested_attribute_errors`

E.X.

```ruby
class Guitar < ActiveRecord::Base
  has_many :tuning_pegs
  accepts_nested_attributes_for :tuning_pegs
end

class TuningPeg < ActiveRecord::Base
  belongs_to :guitar
  validates_numericality_of :pitch
end
```

 - Old style
 - `guitar.errors["tuning_pegs.pitch"] = ["is not a number"]`

 - New style (if defined globally, or set in has_many_relationship)
 - `guitar.errors["tuning_pegs[1].pitch"] = ["is not a number"]`

[Michael Probber, Terence Sun]
@sgrif sgrif merged commit 21e448b into rails:master Oct 26, 2015
sgrif added a commit that referenced this pull request Oct 26, 2015
Errors can be indexed with nested attributes

Close #8638

For models which have nested attributes, errors within those models will
now be indexed if :index_errors is specified when defining a
has_many relationship, or if its set in the global config.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo of it's?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, that is the correct way to spell its here

On Mon, Oct 26, 2015, 5:20 PM Shunsuke Aida notifications@github.com
wrote:

In activerecord/CHANGELOG.md
#19686 (comment):

@@ -1,3 +1,31 @@
+* Add option to index errors in nested attributes
+

  • For models which have nested attributes, errors within those models will
  • now be indexed if :index_errors is specified when defining a
  • has_many relationship, or if its set in the global config.

typo of it's?


Reply to this email directly or view it on GitHub
https://github.com/rails/rails/pull/19686/files#r43067124.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's best you check its meaning 🤓

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider a user reading this. Where would you find "global config?"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its indicates possession.
it's is a contraction for it is.
The latter seems to be the case here. If so, it is would be even better.

@bogdan
Copy link
Contributor

bogdan commented Oct 27, 2015

Excellent stuff guys!

@rweng
Copy link

rweng commented Oct 31, 2015

pulled it in on top of 4.2.4 and it works like a charm. Would love to see this change being in 4.2.5 so I can discard my fork :)

@kaspth
Copy link
Contributor

kaspth commented Oct 31, 2015

Sorry, we don't backport features to patch releases. This'll be in Rails 5.

@uberllama
Copy link
Contributor

Long overdue. Thanks for your effort. 🏁

bbuchalter pushed a commit to TommyJohnWear/solidus that referenced this pull request Nov 18, 2016
  * Using an instance variable allows JS populate operations to have
    access to the @order from within the rendered .js.erb files

  * Assign all line_item errors to the order's base

  * Allow to respond to JS requests for `#populate` and `#update` actions.
    It is left for the developer to actually implement the `js.erb` views

To consider / TODO:

  - to avoid the whole rescue operation, and manually assigning line item
    errors to the order, we'd need to do to be doing `order.save`
    instead of `line_item.save!` in [`OrderContents`](https://github.com/solidusio/solidus/blob/master/core/app/models/spree/order_contents.rb#L151)

  -> That would have the effect to "automatically" assign the line item
     errors on the order in the form: `line_items.quantity` in Rails 4.x
     and "line_items[0].quantity" in Rails 5

  More info:
  - rails/rails#8638
  - rails/rails#19686
kdiogenes added a commit to kdiogenes/active_type that referenced this pull request Feb 7, 2017
@kdiogenes
Copy link

kdiogenes commented Feb 8, 2017

I can't find a way to parse my errors messages:

errors = {
  "categories[0].listener_deadline_values[0].value": ["can't be blank"],
  "categories[0].listener_deadline_values[0].deadline": ["can't be blank"],
  "categories[0].name": ["can't be blank"],
  "categories[1].listener_deadline_values[0].value": ["can't be blank"],
  "categories[1].listener_deadline_values[0].deadline": ["can't be blank"],
  "categories[1].name": ["can't be blank"],
  "categories[2].listener_deadline_values[0].value": ["can't be blank"],
  "categories[2].listener_deadline_values[0].deadline": ["can't be blank"],
  "categories[2].name": ["can't be blank"]
}

I would expect to be able to access these errors like errors.categories[2].name

How do you parse it? I discovered how: https://medium.com/@kdiogenes/giving-super-powers-to-rails-nested-forms-with-vue-js-part-2-acee4a3ee43d

@tommyalvarez
Copy link

Is there a way to index by another criteria @mprobber ? Like the ID of the nested model if it's not a new record? This is because i can't figure a way when using rails 5 api to map errors into several nested children which some of them can have errors while others not, therefore indexes won't match one on one. Or is there another clever solution to handle this situation?

@vidurangaw
Copy link

@tommyalvarez
I'm having the exact issue you've faced. Were you able to find a solution?

@tommyalvarez
Copy link

@vidurangaw i ended up writing my own custom NestedErrorSerializer (i was using active model serializer with json-api adapter). This way i was able to return the errors the way i wanted, respeting json-api format. Thought it would take me a lot of time but it wasn't that hard at all. Here is the implementation i made:

module NestedErrorSerializer
  # object is the object to serialize, relationships an array of symbols with the relationships of the object
  def self.serialize(object, relationships)
    errors = object.errors.messages.map do |field, errors|
      errors.map do |error_message|
        {
          source: {pointer: "/data/attributes/#{field}"},
          detail: error_message
        }
      end
    end
    relationships.each do |relationship|
      object.send(relationship).each_with_index do |child, index|
        errors << child.errors.messages.map do |field, errors|
          errors.map do |error_message|
            {
              source: {pointer: "/data/attributes/#{child.model_name.plural}[#{index}].#{field}"},
              detail: error_message
            }
          end
        end
      end
    end if relationships.present?
    errors.flatten
  end
end

Then in the controller i used them like:

render json: {errors: NestedErrorSerializer.serialize(@product, [:consumables])}, status: :unprocessable_entity

@vidurangaw
Copy link

@tommyalvarez
Thanks for the quick response. I'll try this

@aruprakshit
Copy link

Why this option is not mentioned in the official doc ?

@dhyegocalota
Copy link

I've created a gem (active_record-nested_error_indexer) to monkey patch these changes to Rails 4 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet