-
Notifications
You must be signed in to change notification settings - Fork 21.4k
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
index_errors does not consider indexes of correct existing sub records #24390
Comments
Can you create an executable test script using one of the templates here, that would demonstrate the problem you are seeing? |
begin
require 'bundler/inline'
rescue LoadError => e
$stderr.puts 'Bundler version 1.10 or later is required. Please update your Bundler'
raise e
end
gemfile(true) do
source 'https://rubygems.org'
gem 'rails', github: 'rails/rails'
gem 'sqlite3'
end
require 'active_record'
require 'minitest/autorun'
require 'logger'
# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Schema.define do
create_table :orders, force: true do |t|
end
create_table :entries, force: true do |t|
t.integer :order_id
t.string :title
end
end
class Order < ActiveRecord::Base
has_many :entries, index_errors: true
accepts_nested_attributes_for :entries
end
class Entry < ActiveRecord::Base
belongs_to :order
validates :title, presence: true
end
class BugTest < Minitest::Test
def setup
@order = Order.create!(entries_attributes: [
{ title: 'entry_1' },
{ title: 'entry_2' }])
@entry_1 = @order.entries.first
@entry_2 = @order.entries.last
end
def test_update_both_entries_with_invalid_second
@order.update(entries_attributes: [
{ id: @entry_1.id, title: 'entry_1_updted' },
{ id: @entry_2.id, title: '' }])
assert_equal 1, @order.errors.count
assert_equal 'entries[1].title', @order.errors.messages.keys.first.to_s
end
def test_update_only_second_entry_with_invalid_second
@order.update(entries_attributes: [
{ id: @entry_1.id, title: 'entry_1' },
{ id: @entry_2.id, title: '' }])
assert_equal 1, @order.errors.count
#fails here
assert_equal 'entries[1].title', @order.errors.messages.keys.first.to_s
#BugTest#test_update_only_second_entry_with_invalid_second [active_record_master.rb:64]:
#Expected: "entries[1].title"
#Actual: "entries[0].title"
end
end |
Yep, this definitely looks like a bug to me. Would you be willing to investigate, and open up a pull request with a fix? |
@maclover7 I am willing to give this a try |
Actually errors message is index of Someone can update active_record/autosave_association.rb to fix this. |
I'll try. |
Nope, not as of now.If you have a fix(since you worked on reproduction), do send it out. |
As @zhufenggood mentioned, the error message is using the index of items that pass Changing the method Updated method: def associated_records_to_validate_or_save(association, new_record, autosave)
if new_record || autosave
association && association.target
else
association.target.find_all(&:new_record?)
end
end I have added this commit to my local branch and the ActiveRecord tests are still passing. Happy to help out however I can. Is opening a PR with this commit the correct next step? Sorry, just getting into contributing here. |
Yes @tijwelch if that fixes the issue then opening a PR with a reproduction test of the bug and your fix is the next step. In your pr reference this issue please |
Is this issue resolved? |
@AkshayGoyal022 the PR is still open #24728 |
@tijwelch thanks for the update. Does changing |
any updates on this? I'm still having the same problem in 5.2 |
For those who don't want to monkey patch, I have implemented a hack to overcome this in one of my projects. The idea is frontend needs to send a field (in my case "index") and backend will override the rails index with what was sent from the frontend. Code looks something like this: I made two concerns as the code was being used in multiple models.
This below concern is to reindex the errors. This needs to be included in parent models.
This might not be the best solution but it works fine for my case. |
Instead of current indexes, I'd prefer a solution above with an The solution proposed by @tijwelch causes deadlocks in some circumstances. |
Just stumbled across this issue and decided to share our way of solving this problem.
Usage
It works nicely as long as you can ensure that |
This comment has been minimized.
This comment has been minimized.
I just encountered this issue too. I'm a big fan of nested attributes and was surprised to see that something was broken and has not been fixed for 4 years. I tried the monkey patch above, changing So I tried to fix it myself with another approach. I'm not very familiar with the existing code so this might be a wrong approach too but here it is.
The (wrong) index is created here: the So my solution is to replace this index with the "absolute" index. First I get all the records of the association (whether or not it changed): Here is the full monkey patch: module ActiveRecord
module AutosaveAssociation
def validate_collection_association(reflection)
if association = association_instance_get(reflection.name)
if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave])
all_records = association.target.find_all
records.each do |record|
index = all_records.find_index(record)
association_valid?(reflection, record, index)
end
end
end
end
end
end With this, my specs are good and the right index is returned. It looks like the I will try to submit a PR to get reviews from other contributors and eventually get this in Rails 🙂 |
Thanks! You saved my life. I can not imagine it's near 2021, this bug still exist in RAILS6. And Your patch works pretty fine for me!
|
In your pull request, could you please add the index_errors option to the documentation of the has_many association, and may be refer to it in the accepts_nested_attributes_for method? The option is nowhere documented accept for the Rails 5.0 update readme. Would be great! |
Unfortunately @antoinematyja's solution doesn't work if @AkshayGoyal022's solution in RAILS6 doesn't work The solution is to store the index of the original collection of nested attributes. As it seems to me, the easiest way to do this is by adding a service field (
|
I rewrote @AkshayGoyal022 reindex_nested_error method to work in Rails 6. def reindex_nested_errors
renaming_hash = {}
self.errors.errors.dup.each do |error|
next unless error.class == ActiveModel::NestedError
next unless error.attribute.to_s.include?("].index")
old_key_prefix = error.attribute.to_s.split(".").first
new_index = error.type
new_key_prefix = "#{old_key_prefix.split("[").first}[#{new_index}]"
renaming_hash[old_key_prefix] = new_key_prefix
self.errors.delete(error.attribute)
end
self.errors.errors.dup.each do |error|
next unless error.class == ActiveModel::NestedError
old_key_prefix = error.attribute.to_s.split(".").first
new_key_prefix = renaming_hash[old_key_prefix]
next unless new_key_prefix.present?
new_attribute = error.attribute.to_s.gsub(old_key_prefix, new_key_prefix)
new_error = ActiveModel::NestedError.new(error.base, error.inner_error, { attribute: new_attribute.to_sym })
self.errors.delete(error.attribute)
self.errors.errors.push(new_error)
end
end Also a small update to append_index_to_errors to clear a deprecation warning def append_index_to_errors
if self.errors.present? && self._index.present?
self.errors.add(:index, self._index)
end
end |
@antoinematyja any idea if your fix still works in Rails 7? |
which respects reject_if and is in nested_attributes order. When in default index_errors:true mode, fix rails#24390 and return index based on full association order.
Hi everyone, we in GitLab faced this issue and have created a new fix #48727. It should fix the original issue, while addressing |
which respects reject_if and is in nested_attributes order. When in default index_errors:true mode, fix rails#24390 and return index based on full association order.
which respects reject_if and is in nested_attributes order. When in default index_errors:true mode, fix #24390 and return index based on full association order.
which respects reject_if and is in nested_attributes order. When in default index_errors:true mode, fix rails#24390 and return index based on full association order.
which respects reject_if and is in nested_attributes order. When in default index_errors:true mode, fix rails#24390 and return index based on full association order.
which respects reject_if and is in nested_attributes order. When in default index_errors:true mode, fix rails#24390 and return index based on full association order.
Steps to reproduce
Updating existing record with accept_nested_attributes_for with existing sub-records. (Order - Entries), with index_errors: true.
So, i have existing Order, with 2 existing entries, and try to update it with nested attributes
Expected behavior
Same indexes all times
Actual behavior
If im not changing entry_1, and change entry_2(with incorrect attributes) i got this response:
If im changing entry_1(correct attributes) and change entry_2(incorrect attributes) i got response
System configuration
Rails 5.0.0.beta3
ruby 2.2.3
The text was updated successfully, but these errors were encountered: