# Why does ActiveRecord allow perception of success when updating an ID, however it doesn't really wor

**URL:** <https://www.ruby-forum.com/t/why-does-activerecord-allow-perception-of-success-when-updating-an-id-however-it-doesnt-really-wor/157439>\
**Category:** Rails\
**Created:** [January 11, 2009, 11:09am UTC](https://www.ruby-forum.com/t/why-does-activerecord-allow-perception-of-success-when-updating-an-id-however-it-doesnt-really-wor/157439 "2009-01-11T11:09:29Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![greg](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/g/71c47a/32.png) [@greg](https://www.ruby-forum.com/u/greg)\
**Post date:** [January 11, 2009, 11:09am UTC](https://www.ruby-forum.com/t/why-does-activerecord-allow-perception-of-success-when-updating-an-id-however-it-doesnt-really-wor/157439/1 "2009-01-11T11:09:29Z")

</div>

Hi,  
Why does ActiveRecord allow perception of success when updating an ID,  
however it doesn’t really work(i.e. no change in database)?

Here’s an example. The update of “id” versus update of a non-existent  
attribute.

?\> ai = AccountItem.find(:first) =\> #\<AccountItem id: 1, account\_id: 1,  
date: “2009-01-11”, amount: #BigDecimal:22fb334,‘0.1E2’,4(8), balance:  
#BigDecimal:22fb1f4,‘0.0’,4(8), description: “test”, notes: nil,  
created\_at: “2009-01-11 09:47:28”, updated\_at: “2009-01-11 09:47:28”\> \>\>  
ai.update\_attributes!(:id =\> 99999) =\> true # SEEMS TO INDICATE IT  
WORKED \>\>  
AccountItem.find(:all) =\> [#\<AccountItem id: 1, account\_id: 1, date:  
“2009-01-11”, amount: #BigDecimal:22f27c0,‘0.1E2’,4(8), balance:  
#BigDecimal:22f2680,‘0.0’,4(8), description: “test”, notes: nil,  
created\_at: “2009-01-11 09:47:28”, updated\_at: “2009-01-11 09:47:28”\>] #  
IT  
DIDN’T AS THE ID IS STILL 1 \>\>  
?\> ai.update\_attributes!(:XXXX =\> 99999) NoMethodError: undefined method  
`XXXX=' for #<AccountItem:0x22fc6a8> <== THIS IS A BIT MORE LIKE YOU WOULD EXPECT from /opt/local/lib/ruby/gems/1.8/gems/activerecord-2.1.1/lib/active_record/attribute_methods.rb:251:in`method\_missing’ from  
/opt/local/lib/ruby/gems/1.8/gems/activerecord-2.1.1/lib/active\_record/base.rb:2372:in  
`send' from /opt/local/lib/ruby/gems/1.8/gems/activerecord-2.1.1/lib/active_record/base.rb:2372:in`attributes=’ from  
/opt/local/lib/ruby/gems/1.8/gems/activerecord-2.1.1/lib/active\_record/base.rb:2371:in  
`each' from /opt/local/lib/ruby/gems/1.8/gems/activerecord-2.1.1/lib/active_record/base.rb:2371:in`attributes=’ from  
/opt/local/lib/ruby/gems/1.8/gems/activerecord-2.1.1/lib/active\_record/base.rb:2278:in  
`update\_attributes!’ from (irb):60 \>\>

thanks

---

<div class="post-metadata">

**Author:** ![greg](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/g/71c47a/32.png) [@greg](https://www.ruby-forum.com/u/greg)\
**Post date:** [January 11, 2009, 12:39pm UTC](https://www.ruby-forum.com/t/why-does-activerecord-allow-perception-of-success-when-updating-an-id-however-it-doesnt-really-wor/157439/2 "2009-01-11T12:39:07Z")

</div>

On Sun, Jan 11, 2009 at 9:08 PM, Greg H.  
[removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> Why does ActiveRecord allow perception of success when updating an ID,  
> however it doesn’t really work(i.e. no change in database)?

That is because :id is a special case and is protected from mass  
assignment.

The reason it “works” or says that it does is because when you submit  
an update form, you might have a params hash that looks like:

params[:account] #=\> {:id =\> 2, :name =\> “Bob”}

and you will then do:

a = Account.find(params[:account][:id])  
a.update\_attributes(params[:account])

Now… in 99% of cases, you want this update\_attributes command to  
work, but you definately do NOT want some smartarse out there sending  
in a carefully crafted web request that changes the ID value of the  
account!

So that’s why, update\_attributes “silently” ignores any attribute that  
is protected from mass assignment.

Actually… it doesn’t ignore it. Look in your logs and you will see  
something along the lines of “Can’t mass assign protected attribute  
:id” or something.

For what it’s worth, doing:

account.update\_attribute(:id, 1)

or

account.id = 1  
account.save

Both work.

Hope that helps

Mikel

–

> **[Mikel Lindsaar](https://lindsaar.net/)**
>
> Internet Home for Mikel Lindsaar

Rails, RSpec and Life blog…

---

<div class="post-metadata">

**Author:** ![greg](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/g/71c47a/32.png) [@greg](https://www.ruby-forum.com/u/greg)\
**Post date:** [January 11, 2009, 12:46pm UTC](https://www.ruby-forum.com/t/why-does-activerecord-allow-perception-of-success-when-updating-an-id-however-it-doesnt-really-wor/157439/3 "2009-01-11T12:46:44Z")

</div>

thanks Mike - I was trying to setup a spec that tests that my foreign  
key  
constraints would hold - I’ll try the method you suggest

On Sun, Jan 11, 2009 at 9:38 PM, Mikel L. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
wrote:

> an update form, you might have a params hash that looks like:  
> in a carefully crafted web request that changes the ID value of the
> 
> Hope that helps
> 
> Mikel
> 
> –  
> [http://lindsaar.net/](http://lindsaar.net/)  
> Rails, RSpec and Life blog…
> 
> >

–  
Greg  
[http://blog.gregnet.org/](http://blog.gregnet.org/)

---

<div class="post-metadata">

**Author:** ![greg](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/g/71c47a/32.png) [@greg](https://www.ruby-forum.com/u/greg)\
**Post date:** [January 11, 2009, 12:49pm UTC](https://www.ruby-forum.com/t/why-does-activerecord-allow-perception-of-success-when-updating-an-id-however-it-doesnt-really-wor/157439/4 "2009-01-11T12:49:40Z")

</div>

PS. Just noted the other methods also don’t work fyi - see below. That  
is  
it doesn’t seem to be mass assignment related.  
?\> ai = AccountItem.find(:first)  
=\> #\<AccountItem id: 1, account\_id: 1, date: “2009-01-11”, amount:  
#BigDecimal:22e80b8,‘0.1E2’,4(8), balance:  
#BigDecimal:22e7f78,‘0.0’,4(8), description: “test”, notes: nil,  
created\_at: “2009-01-11 09:47:28”, updated\_at: “2009-01-11 09:47:28”\>

> > ai.id = 100000  
> > =\> 100000  
> > ai  
> > =\> #\<AccountItem id: 100000, account\_id: 1, date: “2009-01-11”, amount:  
> > #BigDecimal:22e34b4,‘0.1E2’,4(8), balance:  
> > #BigDecimal:22e3374,‘0.0’,4(8), description: “test”, notes: nil,  
> > created\_at: “2009-01-11 09:47:28”, updated\_at: “2009-01-11 09:47:28”\>  
> > ai.save  
> > =\> true

?\> AccountItem.find(:all)  
=\> [#\<AccountItem id: 1, account\_id: 1, date: “2009-01-11”, amount:  
#BigDecimal:22db098,‘0.1E2’,4(8), balance:  
#BigDecimal:22daf58,‘0.0’,4(8), description: “test”, notes: nil,  
created\_at: “2009-01-11 09:47:28”, updated\_at: “2009-01-11 09:47:28”\>]

> >

On Sun, Jan 11, 2009 at 9:38 PM, Mikel L. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
wrote:

> an update form, you might have a params hash that looks like:  
> in a carefully crafted web request that changes the ID value of the
> 
> Hope that helps
> 
> Mikel
> 
> –  
> [http://lindsaar.net/](http://lindsaar.net/)  
> Rails, RSpec and Life blog…
> 
> >

–  
Greg  
[http://blog.gregnet.org/](http://blog.gregnet.org/)

---

<div class="post-metadata">

**Author:** ![greg](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/g/71c47a/32.png) [@greg](https://www.ruby-forum.com/u/greg)\
**Post date:** [January 11, 2009, 1:33pm UTC](https://www.ruby-forum.com/t/why-does-activerecord-allow-perception-of-success-when-updating-an-id-however-it-doesnt-really-wor/157439/5 "2009-01-11T13:33:16Z")

</div>

> a = Account.find(1)  
> new\_account = Account.new(a.params)  
> new\_account.id = new\_id  
> new\_account.save
> 
> _should_ work 🙂

Well… it most definitely won’t…

You need:

new\_account = Account.new(a.attributes)

and you might get further

–

> **[Mikel Lindsaar](https://lindsaar.net/)**
>
> Internet Home for Mikel Lindsaar

Rails, RSpec and Life blog…

---

<div class="post-metadata">

**Author:** ![greg](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/g/71c47a/32.png) [@greg](https://www.ruby-forum.com/u/greg)\
**Post date:** [January 11, 2009, 9:36pm UTC](https://www.ruby-forum.com/t/why-does-activerecord-allow-perception-of-success-when-updating-an-id-however-it-doesnt-really-wor/157439/6 "2009-01-11T21:36:58Z")

</div>

thanks - given all of this what do you think about the fact Rails  
doesn’t  
highlight the issue via exception? It still seems misleading to me the  
way  
it works…

On Sun, Jan 11, 2009 at 10:32 PM, Mikel L. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
wrote:

> You need:
> 
> >

–  
Greg  
[http://blog.gregnet.org/](http://blog.gregnet.org/)

---

<div class="post-metadata">

**Author:** ![greg](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/g/71c47a/32.png) [@greg](https://www.ruby-forum.com/u/greg)\
**Post date:** [January 11, 2009, 1:32pm UTC](https://www.ruby-forum.com/t/why-does-activerecord-allow-perception-of-success-when-updating-an-id-however-it-doesnt-really-wor/157439/7 "2009-01-11T13:32:40Z")

</div>

Now… that is interesting

To be honest, I’ve only ever used setting the id to a specific value  
on a new record… not on an existing one.

The problem is that the following code in  
rails/activerecord/lib/base.rb is what does the update:

```
  def update(attribute_names = @attributes.keys)
    quoted_attributes = attributes_with_quotes(false, false,

```

attribute\_names)  
return 0 if quoted\_attributes.empty?  
connection.update(  
"UPDATE #{self.class.quoted\_table\_name} " +  
"SET #{quoted\_comma\_pair\_list(connection, quoted\_attributes)}  
" +  
“WHERE  
#{connection.quote\_column\_name(self.class.primary\_key)} =  
#{quote\_value(id)}”,  
“#{self.class.name} Update”  
)  
end

As you can see, the WHERE clause is going to use id to find the record  
we are after, as you set id to a different value… that won’t  
work…

This code would have to be updated to allow id to be set using a  
“new\_id” and “old\_id” value pair…

I think this is enough of an edge case though that you would be better  
served by making a new record and deleting the old one…

like so:

a = Account.find(1)  
new\_account = Account.new(a.params)  
new\_account.id = new\_id  
new\_account.save

_should_ work 🙂

Mikel

–

> **[Mikel Lindsaar](https://lindsaar.net/)**
>
> Internet Home for Mikel Lindsaar

Rails, RSpec and Life blog…
