Update multiple records with a list of ids


#1

This is a beginner-ish question, I know, but I can’t seem to find
example code anywhere that resembles what I’m doing. The line I can’t
get working is this:

@comments = Comment.update @params['comments'].keys, :status => 

status

status has been set to the string ‘approve’, and
@params[‘comments’].keys returns a list of valid ids, but when I run the
code, I get this in my logs:

Comment Load (0.004792) SELECT * FROM comments WHERE comments.id =
‘2’ LIMIT 1
SQL (0.000320) BEGIN
Comment Columns (0.000637) SHOW FIELDS FROM comments
Comment Update (0.000752) UPDATE comments SET date = ‘2005-10-30
18:29:11’, comment = ‘something else’, status = ‘moderate’,
user_id = 1, post_id = 1 WHERE id = 2
SQL (0.000266) COMMIT

In other words, it’s ignoring the ‘:status => status’ argument. I
suspect I’m messing up the syntax, but reading through the
ActiveRecord::Base source doesn’t give me any tips on how to do it
properly. Any suggestions?

Joel


#2

here’s the source for AR update method:

# File vendor/rails/activerecord/lib/active_record/base.rb, line 473

473: def update(id, attributes)
474: if id.is_a?(Array)
475: idx = -1
476: id.collect { |id| idx += 1; update(id, attributes[idx]) }
477: else
478: object = find(id)
479: object.update_attributes(attributes)
480: object
481: end
482: end

if you have id = [1,2,3], and attributes = { :status => “approve” }

the way i read this, update will do:

if id is an array set counter idx = -1, then for each id, increment idx
by 1
and update current id with attributes value at index idx

id is array, idx = -1
id = 1, idx = 0, update(1, attributes[0])
id = 2, idx = 1, update(2, attributes[1])
id = 3, idx = 2, update(3, attributes[2])

which is equivalent to:

id is array, idx = -1
id = 1, idx = 0, update(1, {:status => “approved”)
id = 2, idx = 1, update(2, nil)
id = 3, idx = 2, update(3, nil)

which makes sense that nothing is getting updated after the first
record…

so what’s happening is that it appears update assumes attributes to be
an
array of hashes matching the size of the id array IF id is an array.

so what you want to do is…

statuses = Array.new(@params[‘comments’].size, { :status => “approved”
})
@comments = Comment.update(@params[‘comments’].keys, statuses)

see if this works

Chris


#3

gah, the formatting for the source is messed up

second try…

Finds the record from the passed +id+, instantly saves it with the

passed
+attributes+ (if the validation permits it),

and returns it. If the save fail under validations, the unsaved object

is
still returned.
def update(id, attributes)
if id.is_a?(Array)
idx = -1
id.collect { |id| idx += 1; update(id, attributes[idx]) }
else
object = find(id)
object.update_attributes(attributes)
object
end
end


#4

christopher.k.hall wrote:

so what you want to do is…

statuses = Array.new(@params[‘comments’].size, { :status => “approved”
})
@comments = Comment.update(@params[‘comments’].keys, statuses)

see if this works

Thanks, that was the ticket! I actually decided to just loop through it
myself, like so:

@params['comments'].keys.each { |id| Comment.update id, :status => 

status }

Since this is a comment moderation system with a potentially large
number of comments being approved/denied (especially the latter) at
once, I think I’ll change it to update_all with a list of ids. In any
case, thanks for clearing that up! I don’t know how I missed the fact
that attributes has to be an array of hashes.

Joel