New #update_columns method. #1190

Closed
wants to merge 3 commits into from

8 participants

@smartinez87

Some time ago I pushed the #update_column method, and it can be seen here 245542e#commitcomment-384953 how someone asked for #update_columns, which to me makes sense for completeness and consistency.

So here it is :)

@pixeltrix
Ruby on Rails member

What about mass-assignment security?

@smartinez87

I don't see this method being used say on the controller passing the params hash, where you definitely need mass-assignment security. It is not intended to be a replacement of #update_attributes.
I see it being used say in your logic where you need to set a couple of attributes, without even executing callbacks or validations.

@dasch dasch commented on an outdated diff May 25, 2011
activerecord/lib/active_record/persistence.rb
@@ -169,6 +169,23 @@ module ActiveRecord
end
end
+ # Updates the attributes from the passed-in hash, without calling save.
+ #
+ # * Validation is skipped.
+ # * Callbacks are skipped.
+ # * +updated_at+/+updated_on+ column is not updated if that column is available.
+ #
+ # Raises an +ActiveRecordError+ when called on new objects, or when at least
+ # one if the attributes is marked as readonly.
@dasch
dasch added a note May 25, 2011

You probably mean "one of", not "one if".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@TheEmpty

I agree, if it's bypassing validations and callbacks, it should by pass everything. Yeah this is pretty nice, but update_attribute doesn't activate callbacks or validations either does it? Because I've used that several times to bypass validations and what not; so either I'm crazy or this would do the same thing?

Also I'm reading a nice testing book now, is Topic a stub or is that really coming from the database? I would imagine with Rails those kind of things can make the tests much faster.

@smartinez87

you're right @dasch, thanks

@henrikhodne

I think this should probably bypass everything too.

@rafaelfranca
Ruby on Rails member

This pull request can't be automatically merge.

If you still want to make this one part of the Rails please rebase it.

@smartinez87

@rafaelfranca just rebased it. Also added the entry to the CHANGELOG for it.
Thanks!

@rafaelfranca
Ruby on Rails member
@tenderlove
Ruby on Rails member

What is the difference between this and assign_attributes?

@smartinez87

The main difference I see is that update_columns impacts the database.
Note that this pull request was made to have completeness with #update_column, that people was asking for when it was merged.

@rgarver

This is a good addition since there is not an easy alternative except multiple update_column calls.

@tenderlove
Ruby on Rails member

Could this be implemented as:

def update_columns(attributes)
  assign_attributes(attributes)
  save
end
@smartinez87

@tenderlove Wouldn't calling save execute callbacks and validations?
perhaps something like this:

def update_columns(attributes)
  raise ActiveRecordError, "can not update on a new record object" unless persisted?
  attributes.each_key {|key| raise ActiveRecordError, "#{key.to_s} is marked as readonly" if self.class.readonly_attributes.include?(key.to_s) }
  assign_attributes(attributes)
  self.class.update_all(attributes, self.class.primary_key => id) == 1
end
@sobrinho

Any news about this pull request?

Currently we needed that on a migration and workarounded using this:

ResourceAnnul.find_each do |annul|
  # annul.update_column :resource_id, annul.price_collection_proposal_id
  # annul.update_column :resource_type, 'PriceCollectionProposal'

  ResourceAnnul.update_all( {:resource_id => annul.price_collection_proposal_id, :resource_type => 'PriceCollectionProposal'}, {:id => annul.id} )
end

I think the intention of update_columns is update without hitting validations and/or callbacks.

Something like:

  • update_attributes - hit validations, hit callbacks, respect mass assignment
  • update_attribute - ignore validations, hit callbacks, ignore mass assignment
  • update_columns - ignore validations, ignore callbacks, ignore mass assignment
  • update_column - ignore validations, ignore callbacks, ignore mass assignment

But the difference is if you need to update more than one attribute and don't want to querying one time per attribute like happened on my migration.

What you think, @tenderlove ?

@rafaelfranca
Ruby on Rails member

I don't see the value to still have the update_column since we are adding a method that can be used to one or more fields.

That said I proposing the following path:

  • update_attributes - hit validations, hit callbacks, respect mass assignment
  • update_columns - ignore validations, ignore callbacks, ignore mass assignment

update_attribute was removed in 4.0 (#6738) and deprecated in 3-2-stable (#6739)
update_column will be deprecated in 4.0 and remove in the next major release.

I'll work on this.

@rafaelfranca
Ruby on Rails member

Done @ 864b49d

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment