mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Add an :update_only option to accepts_nested_attributes_for for to-one associations. [#2563 state:resolved]
Signed-off-by: Eloy Duran <eloy.de.enige@gmail.com>
This commit is contained in:
parent
9c771a9608
commit
07b615fb89
2 changed files with 54 additions and 3 deletions
|
@ -212,6 +212,11 @@ module ActiveRecord
|
||||||
# nested attributes array exceeds the specified limit, NestedAttributes::TooManyRecords
|
# nested attributes array exceeds the specified limit, NestedAttributes::TooManyRecords
|
||||||
# exception is raised. If omitted, any number associations can be processed.
|
# exception is raised. If omitted, any number associations can be processed.
|
||||||
# Note that the :limit option is only applicable to one-to-many associations.
|
# Note that the :limit option is only applicable to one-to-many associations.
|
||||||
|
# [:update_only]
|
||||||
|
# Allows to specify that the an existing record can only be updated.
|
||||||
|
# A new record in only created when there is no existing record. This
|
||||||
|
# option only works for on-to-one associations and is ignored for
|
||||||
|
# collection associations. This option is off by default.
|
||||||
#
|
#
|
||||||
# Examples:
|
# Examples:
|
||||||
# # creates avatar_attributes=
|
# # creates avatar_attributes=
|
||||||
|
@ -221,9 +226,9 @@ module ActiveRecord
|
||||||
# # creates avatar_attributes= and posts_attributes=
|
# # creates avatar_attributes= and posts_attributes=
|
||||||
# accepts_nested_attributes_for :avatar, :posts, :allow_destroy => true
|
# accepts_nested_attributes_for :avatar, :posts, :allow_destroy => true
|
||||||
def accepts_nested_attributes_for(*attr_names)
|
def accepts_nested_attributes_for(*attr_names)
|
||||||
options = { :allow_destroy => false }
|
options = { :allow_destroy => false, :update_only => false }
|
||||||
options.update(attr_names.extract_options!)
|
options.update(attr_names.extract_options!)
|
||||||
options.assert_valid_keys(:allow_destroy, :reject_if, :limit)
|
options.assert_valid_keys(:allow_destroy, :reject_if, :limit, :update_only)
|
||||||
|
|
||||||
attr_names.each do |association_name|
|
attr_names.each do |association_name|
|
||||||
if reflection = reflect_on_association(association_name)
|
if reflection = reflect_on_association(association_name)
|
||||||
|
@ -288,6 +293,13 @@ module ActiveRecord
|
||||||
# record’s id, then the existing record will be modified. Otherwise a new
|
# record’s id, then the existing record will be modified. Otherwise a new
|
||||||
# record will be built.
|
# record will be built.
|
||||||
#
|
#
|
||||||
|
# If update_only is true, a new record is only created when no object exists,
|
||||||
|
# otherwise it will be updated
|
||||||
|
#
|
||||||
|
# If update_only is false and the given attributes include an <tt>:id</tt>
|
||||||
|
# that matches the existing record’s id, then the existing record will be
|
||||||
|
# modified. Otherwise a new record will be built.
|
||||||
|
#
|
||||||
# If the given attributes include a matching <tt>:id</tt> attribute _and_ a
|
# If the given attributes include a matching <tt>:id</tt> attribute _and_ a
|
||||||
# <tt>:_destroy</tt> key set to a truthy value, then the existing record
|
# <tt>:_destroy</tt> key set to a truthy value, then the existing record
|
||||||
# will be marked for destruction.
|
# will be marked for destruction.
|
||||||
|
@ -295,7 +307,15 @@ module ActiveRecord
|
||||||
options = self.nested_attributes_options[association_name]
|
options = self.nested_attributes_options[association_name]
|
||||||
attributes = attributes.with_indifferent_access
|
attributes = attributes.with_indifferent_access
|
||||||
|
|
||||||
if attributes['id'].blank?
|
if options[:update_only]
|
||||||
|
if existing_record = send(association_name)
|
||||||
|
assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
|
||||||
|
else
|
||||||
|
unless reject_new_record?(association_name, attributes)
|
||||||
|
send("build_#{association_name}", attributes.except(*UNASSIGNABLE_KEYS))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elsif attributes['id'].blank?
|
||||||
unless reject_new_record?(association_name, attributes)
|
unless reject_new_record?(association_name, attributes)
|
||||||
method = "build_#{association_name}"
|
method = "build_#{association_name}"
|
||||||
if respond_to?(method)
|
if respond_to?(method)
|
||||||
|
|
|
@ -245,6 +245,37 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
|
||||||
def test_should_automatically_enable_autosave_on_the_association
|
def test_should_automatically_enable_autosave_on_the_association
|
||||||
assert Pirate.reflect_on_association(:ship).options[:autosave]
|
assert Pirate.reflect_on_association(:ship).options[:autosave]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_should_accept_update_only_option
|
||||||
|
Pirate.accepts_nested_attributes_for :ship, :update_only => true
|
||||||
|
@pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :name => 'Mayflower' })
|
||||||
|
|
||||||
|
Pirate.accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_should_create_new_model_when_nothing_is_there_and_update_only_is_true
|
||||||
|
Pirate.accepts_nested_attributes_for :ship, :update_only => true
|
||||||
|
@ship.delete
|
||||||
|
|
||||||
|
assert_difference('Ship.count', 1) do
|
||||||
|
@pirate.reload.update_attribute(:ship_attributes, { :name => 'Mayflower' })
|
||||||
|
end
|
||||||
|
|
||||||
|
Pirate.accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_update_existing_when_update_only_is_true_and_no_id_is_given
|
||||||
|
Pirate.accepts_nested_attributes_for :ship, :update_only => true
|
||||||
|
|
||||||
|
assert_no_difference('Ship.count') do
|
||||||
|
@pirate.reload.update_attributes(:ship_attributes => { :name => 'Mayflower' })
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_equal 'Mayflower', @ship.reload.name
|
||||||
|
|
||||||
|
Pirate.accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
|
class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
|
||||||
|
|
Loading…
Reference in a new issue