Question about accepts_nested_attributes_for and reject_if

24 views
Skip to first unread message

Sam Kong

unread,
Jan 25, 2011, 12:44:27 AM1/25/11
to rubyonra...@googlegroups.com
Hi,

I am developing a rails 3.0.3 application and
accepts_nested_attributes_for method is giving me pains.

To simplify the issue, I created a new app and generated 2 models.

users
--------
name: string

cars
------
user_id: integer
name: string

class User < ActiveRecord::Base
has_many :cars

accepts_nested_attributes_for :cars, :allow_destroy => true,
:reject_if => proc { |attrs| attrs['name'].blank? }
end

class Car < ActiveRecord::Base
belongs_to :user
end


Very simple, huh?

In console,

I created a user and create 2 cars for the user.

u = User.first
u.cars_attributes={"0"=>{"id"=>2, "_destroy"=>"1"}}
u.save

This should destroy the car but didn't.
If I modify the User model like

accepts_nested_attributes_for :cars, :allow_destroy => true

Then, it works as I expect meaning it destroy the car with the same code
in the console.

If I modify the line like the following, it works also.

accepts_nested_attributes_for :cars, :allow_destroy => true, :reject_if
=> proc { |attrs| attrs['id'].blank? and attrs['name'].blank? }

As I understand it, reject_if option is only for new instance not for
destroyed instance.
Am I wrong?


Sam

--
Posted via http://www.ruby-forum.com/.

Philip Hallstrom

unread,
Jan 25, 2011, 12:17:31 PM1/25/11
to rubyonra...@googlegroups.com

The docs seem to contradict each other. First...

# You may also set a :reject_if proc to silently ignore any new record
# hashes if they fail to pass your criteria. For example, the previous
# example could be rewritten as:

But then....

# Allows you to specify a Proc or a Symbol pointing to a method
# that checks whether a record should be built for a certain attribute
# hash. The hash is passed to the supplied Proc or the method
# and it should return either +true+ or +false+. When no :reject_if
# is specified, a record will be built for all attribute hashes that
# do not have a <tt>_destroy</tt> value that evaluates to true.
# Passing <tt>:all_blank</tt> instead of a Proc will create a proc
# that will reject a record where all the attributes are blank.

The code says the second is true... from active_record/nested_attributes.rb around line 376...

It loops through all the nested attributes...
- if the 'id' is blank and we don't reject the record, then build it.
- else we have an 'id' so find the record and if we don't reject it, add it to the target and *then* mark it for destruction.

Unless I'm reading it wrong :)

attributes_collection.each do |attributes|
attributes = attributes.with_indifferent_access

if attributes['id'].blank?
unless reject_new_record?(association_name, attributes)
association.build(attributes.except(*UNASSIGNABLE_KEYS))
end

elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes['id'].to_s }
association.send(:add_record_to_target_with_callbacks, existing_record) if !association.loaded? && !call_reject_if(association_name, attributes)
assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])

else
raise_nested_attributes_record_not_found(association_name, attributes['id'])
end
end


Reply all
Reply to author
Forward
0 new messages