mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Merge pull request #19686 from tsun1215/index_errors
Errors can be indexed with nested attributes Close #8638
This commit is contained in:
commit
118232aef4
8 changed files with 95 additions and 4 deletions
|
@ -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.
|
||||
|
||||
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 and Terence Sun*
|
||||
|
||||
* Exit with non-zero status for failed database rake tasks.
|
||||
|
||||
*Jay Hayes*
|
||||
|
|
|
@ -5,7 +5,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|||
end
|
||||
|
||||
def self.valid_options(options)
|
||||
super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of, :counter_cache, :join_table, :foreign_type]
|
||||
super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of, :counter_cache, :join_table, :foreign_type, :index_errors]
|
||||
end
|
||||
|
||||
def self.valid_dependent_options
|
||||
|
|
|
@ -140,6 +140,8 @@ module ActiveRecord
|
|||
|
||||
included do
|
||||
Associations::Builder::Association.extensions << AssociationBuilderExtension
|
||||
mattr_accessor :index_nested_attribute_errors, instance_writer: false
|
||||
self.index_nested_attribute_errors = false
|
||||
end
|
||||
|
||||
module ClassMethods # :nodoc:
|
||||
|
@ -315,7 +317,7 @@ module ActiveRecord
|
|||
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])
|
||||
records.each { |record| association_valid?(reflection, record) }
|
||||
records.each_with_index { |record, index| association_valid?(reflection, record, index) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -323,14 +325,18 @@ module ActiveRecord
|
|||
# Returns whether or not the association is valid and applies any errors to
|
||||
# the parent, <tt>self</tt>, if it wasn't. Skips any <tt>:autosave</tt>
|
||||
# enabled records if they're marked_for_destruction? or destroyed.
|
||||
def association_valid?(reflection, record)
|
||||
def association_valid?(reflection, record, index=nil)
|
||||
return true if record.destroyed? || (reflection.options[:autosave] && record.marked_for_destruction?)
|
||||
|
||||
validation_context = self.validation_context unless [:create, :update].include?(self.validation_context)
|
||||
unless valid = record.valid?(validation_context)
|
||||
if reflection.options[:autosave]
|
||||
record.errors.each do |attribute, message|
|
||||
attribute = "#{reflection.name}.#{attribute}"
|
||||
if index.nil? || (!reflection.options[:index_errors] && !ActiveRecord::Base.index_nested_attribute_errors)
|
||||
attribute = "#{reflection.name}.#{attribute}"
|
||||
else
|
||||
attribute = "#{reflection.name}[#{index}].#{attribute}"
|
||||
end
|
||||
errors[attribute] << message
|
||||
errors[attribute].uniq!
|
||||
end
|
||||
|
|
|
@ -24,6 +24,8 @@ require 'models/molecule'
|
|||
require 'models/member'
|
||||
require 'models/member_detail'
|
||||
require 'models/organization'
|
||||
require 'models/guitar'
|
||||
require 'models/tuning_peg'
|
||||
|
||||
class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
|
||||
def test_autosave_validation
|
||||
|
@ -397,6 +399,40 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttrib
|
|||
assert_not molecule.persisted?, 'Molecule should not be persisted when its electrons are invalid'
|
||||
end
|
||||
|
||||
def test_errors_should_be_indexed_when_passed_as_array
|
||||
guitar = Guitar.new
|
||||
tuning_peg_valid = TuningPeg.new
|
||||
tuning_peg_valid.pitch = 440.0
|
||||
tuning_peg_invalid = TuningPeg.new
|
||||
|
||||
guitar.tuning_pegs = [tuning_peg_valid, tuning_peg_invalid]
|
||||
|
||||
assert_not tuning_peg_invalid.valid?
|
||||
assert tuning_peg_valid.valid?
|
||||
assert_not guitar.valid?
|
||||
assert_equal ["is not a number"], guitar.errors["tuning_pegs[1].pitch"]
|
||||
assert_not_equal ["is not a number"], guitar.errors["tuning_pegs.pitch"]
|
||||
end
|
||||
|
||||
def test_errors_should_be_indexed_when_global_flag_is_set
|
||||
old_attribute_config = ActiveRecord::Base.index_nested_attribute_errors
|
||||
ActiveRecord::Base.index_nested_attribute_errors = true
|
||||
|
||||
molecule = Molecule.new
|
||||
valid_electron = Electron.new(name: 'electron')
|
||||
invalid_electron = Electron.new
|
||||
|
||||
molecule.electrons = [valid_electron, invalid_electron]
|
||||
|
||||
assert_not invalid_electron.valid?
|
||||
assert valid_electron.valid?
|
||||
assert_not molecule.valid?
|
||||
assert_equal ["can't be blank"], molecule.errors["electrons[1].name"]
|
||||
assert_not_equal ["can't be blank"], molecule.errors["electrons.name"]
|
||||
ensure
|
||||
ActiveRecord::Base.index_nested_attribute_errors = old_attribute_config
|
||||
end
|
||||
|
||||
def test_valid_adding_with_nested_attributes
|
||||
molecule = Molecule.new
|
||||
valid_electron = Electron.new(name: 'electron')
|
||||
|
|
4
activerecord/test/models/guitar.rb
Normal file
4
activerecord/test/models/guitar.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
class Guitar < ActiveRecord::Base
|
||||
has_many :tuning_pegs, index_errors: true
|
||||
accepts_nested_attributes_for :tuning_pegs
|
||||
end
|
4
activerecord/test/models/tuning_peg.rb
Normal file
4
activerecord/test/models/tuning_peg.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
class TuningPeg < ActiveRecord::Base
|
||||
belongs_to :guitar
|
||||
validates_numericality_of :pitch
|
||||
end
|
|
@ -348,6 +348,10 @@ ActiveRecord::Schema.define do
|
|||
t.column :key, :string
|
||||
end
|
||||
|
||||
create_table :guitar, force: true do |t|
|
||||
t.string :color
|
||||
end
|
||||
|
||||
create_table :inept_wizards, force: true do |t|
|
||||
t.column :name, :string, null: false
|
||||
t.column :city, :string, null: false
|
||||
|
@ -851,6 +855,11 @@ ActiveRecord::Schema.define do
|
|||
t.belongs_to :ship
|
||||
end
|
||||
|
||||
create_table :tuning_pegs, force: true do |t|
|
||||
t.integer :guitar_id
|
||||
t.float :pitch
|
||||
end
|
||||
|
||||
create_table :tyres, force: true do |t|
|
||||
t.integer :car_id
|
||||
end
|
||||
|
|
|
@ -313,6 +313,10 @@ All these configuration options are delegated to the `I18n` library.
|
|||
by a query exceeds the threshold, a warning is logged. This can be used to
|
||||
identify queries which might be causing memory bloat.
|
||||
|
||||
* `config.active_record.index_nested_attribute_errors` allows errors for nested
|
||||
has_many relationships to be displayed with an index as well as the error.
|
||||
Defaults to false.
|
||||
|
||||
The MySQL adapter adds one additional configuration option:
|
||||
|
||||
* `ActiveRecord::ConnectionAdapters::MysqlAdapter.emulate_booleans` controls whether Active Record will consider all `tinyint(1)` columns in a MySQL database to be booleans and is true by default.
|
||||
|
|
Loading…
Reference in a new issue