aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
authorMichael Siebert <siebertm85@googlemail.com>2009-12-28 19:02:01 +0100
committerEloy Duran <eloy.de.enige@gmail.com>2009-12-28 21:09:19 +0100
commit07b615fb897017d7acfaafa88606bc88be30f6e4 (patch)
treed4d68e6f8a322e0309dca307f8cdbe018f9cd6f0 /activerecord
parent9c771a9608f54ebdfcb6fca819c83038489ce50d (diff)
downloadrails-07b615fb897017d7acfaafa88606bc88be30f6e4.tar.gz
rails-07b615fb897017d7acfaafa88606bc88be30f6e4.tar.bz2
rails-07b615fb897017d7acfaafa88606bc88be30f6e4.zip
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>
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb26
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb31
2 files changed, 54 insertions, 3 deletions
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index 386f0e7ca7..5143e804d1 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -212,6 +212,11 @@ module ActiveRecord
# nested attributes array exceeds the specified limit, NestedAttributes::TooManyRecords
# exception is raised. If omitted, any number associations can be processed.
# 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:
# # creates avatar_attributes=
@@ -221,9 +226,9 @@ module ActiveRecord
# # creates avatar_attributes= and posts_attributes=
# accepts_nested_attributes_for :avatar, :posts, :allow_destroy => true
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.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|
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 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
# <tt>:_destroy</tt> key set to a truthy value, then the existing record
# will be marked for destruction.
@@ -295,7 +307,15 @@ module ActiveRecord
options = self.nested_attributes_options[association_name]
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)
method = "build_#{association_name}"
if respond_to?(method)
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index 5367907fb7..5e4fc2b8d9 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -245,6 +245,37 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
def test_should_automatically_enable_autosave_on_the_association
assert Pirate.reflect_on_association(:ship).options[:autosave]
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
class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase