ActiveRecord: when exactly is a record (model) saved to the database?

Hello,
i am playing with some test application in console: creating a model,
saving it, changing its id, trying to save it again, changing the id
back, saving again, etc.
The results are sometimes unexpected, but maybe i just do not understand
how/when the records are saved.

Can anybody please tell me if calling the “save” method saves the model
immediately?
If not, what is the method to save it immediately?

I keep an Sqlite database browser open (a Firefox extension), and it
seems that some “save” calls have no effect, but after changing some
attributes, the “save” works again.
I wonder if it is because of my messing around with id, or just the data
base does not get updated immediately.

Thanks.

i am playing with some test application in console: creating a model,
saving it, changing its id, trying to save it again, changing the id

Why are you changing it’s id? You probably shouldn’t be doing that…

attributes, the “save” works again.
I wonder if it is because of my messing around with id, or just the data
base does not get updated immediately.

Do you have any validations on the model that might be failing? save
will return false if validations fail, but won’t throw a fit… and
obviously won’t save the record.

Try save! which will throw a fit. Or ask the model for it’s errors…

-philip

On 29 Mar 2011, at 19:37, “Alexey M.” [email protected] wrote:

Hello,
i am playing with some test application in console: creating a model,
saving it, changing its id, trying to save it again, changing the id
back, saving again, etc.
The results are sometimes unexpected, but maybe i just do not understand
how/when the records are saved.

Can anybody please tell me if calling the “save” method saves the model
immediately?

It will, as long as validations passed and at least some attributes have
changed. Changing the id could conceivably cause odd things.

Fred

Use google to search for: around_save yield

yield be inside the method around_save

Everything is before yield is before save the record
All is after yield is after save the record

def around_save
#do anything before save
yield
# do anything after save
end

2011/3/29 Alexey M. [email protected]


You received this message because you are subscribed to the Google G.
“Ruby on Rails: Talk” group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/rubyonrails-talk?hl=en.


Rodrigo M.
(62) 8567-3142

I do not have any validations, the model is basically empty (only
attributes and associations).

save! returns true, but doesn’t save anything, but saves later, after
changing more attributes …

So, i assume this could be because of changing the id.
But what the method id= is for then?
I may post later (tomorrow?) a minimal example.

Maybe you are changing the attribute in place thus the change is not
saved ?

Read:
“If an attribute is modified in-place then make use of
[attribute_name]_will_change! to mark that the attribute is changing.
Otherwise ActiveModel cant track changes to in-place attributes.”

Maybe you don’t see the change immediately because transaction is
still going ?

Robert Pankowecki

On 29 Mar 2011, at 19:53, Alexey M. [email protected] wrote:

I do not have any validations, the model is basically empty (only
attributes and associations).

save! returns true, but doesn’t save anything, but saves later, after
changing more attributes …

So, i assume this could be because of changing the id.
But what the method id= is for then?

I’ve only ever used it when I wanted a new record to have a specific id.

Fred

Robert Pankowecki wrote in post #989839:

Maybe you are changing the attribute in place thus the change is not
saved ?

This is possible, thanks for pointing this out, i will test.

Robert, it does not seem like i was changing attributes in-place.

Here is what is going on, i do not understand how it can be a desired
behavior:

I created a test application:

$ rails new test_app
$ cd test_app
$ rails generate model Person name:string age:integer
$ rake db:migrate

In console:

p = Person.create(:name=>“Johny”, :age=>30)
p.id # => 1
p.id = 2 # => 2
p.save # => true
No change in the database!
p.id # => 2
p.name = “Jim” # => “Jim”
p.save # => true
No change in the database
p.name # => “Jim”
p.id = 1 # => 1
p.save # => true
No change in the database
p.age = 31 # => 31
p.save # => true
In the database, the age became 31, but the name stayed “Johny”!
p.name # => “Jim”
p.name.object_id # => 2155015240
p.name = “Jim” # => “Jim”
p.name.object_id # => 2154887680
p.save # => true
No change in the database!
p.name = “Jimmy” # => “Jimmy”
p.name.object_id # => 2154859980
p.save # => true
Finally, the name changed in the database!

Alexey.

On Mar 30, 11:37am, Alexey M. [email protected] wrote:

Robert, it does not seem like i was changing attributes in-place.

Here is what is going on, i do not understand how it can be a desired
behavior:

The core isssue is that changing id in activerecord is meaningless:
every update statement is of the form (simplifying slightly)

update blah set … where id = #{self.id}

So changing self.id will either update no rows or update some existing
object.

The second thing coming into play is dirty attributes: activerecord
only includes changed attribute in the update clause, so if it thinks
that no attributes have changed since the last save it won’t do
anything.

Why are you changing the id of an existing record?

Fred

p.id # => 1
p.id = 2 # => 2
p.save # => true

On 30 March 2011 12:04, Alexey M. [email protected] wrote:

Frederick C. wrote in post #989934:

Why are you changing the id of an existing record?

Just to learn how it behaves.
I also plan an application where i want to use the primary key as a
foreign key for a has_one association, so there it could (possibly) make
sense to change it.

I told you life would be much easier for you if you stuck to the Rails
conventions :slight_smile:

Colin

Colin, i shall always listen to you better in the future.
However, now i am taking this challenge personally.

Frederic, i think you are right, my problems/confusions are arising from
the fact that on subsequent calls save uses UPDATE instead of INSERT.
Is there a way to force it to use INSERT if the id is not used by
another record?

Alexey.

After some thinking, i agree that there is no real need to ever change
the primary key (the reason i want to use it in my application also as a
foreign key is exactly that there will be no need to change this foreign
key).
If i need to copy attributes from one record to another, i can do it
with attributes=().
Thanks for all the explanations.

Frederick C. wrote in post #989934:

Why are you changing the id of an existing record?

Just to learn how it behaves.
I also plan an application where i want to use the primary key as a
foreign key for a has_one association, so there it could (possibly) make
sense to change it.

If assigning anything to the primary key breaks ActiveRecord, is there a
way to disable the id=() method after a record has been created?

Alexey.

On Thu, Mar 31, 2011 at 9:06 AM, Alexey M.
[email protected]wrote:

p.save # => true
Nothing is saved in the database, but what disturbs me more is that
“save” returned true in such case.

In this first example you are inializing an object instance of Person by
calling the “create” method. The “create” method of class Person does a
“create” to the database where as the method “new” does not. The
“destroy”
method you called sends a destroy to the database for the record based
on
that id but does not destroy the obect instance. This is because object
is
not a pointer to the database record, it’s an instance of the class
Person.
This is why you can still retrieve the name from your object instance
later
by calling the method “name”. The “save” method should be doing the
action
of create or update in the database (depending on if the “new” or
“create”
methods were called to inialize the object instance). It returning
“true” is
strange since the record in the database is neither being created or
updated. That may indeed be a bug.

In this example you are inializing two seperate object instances of the
class Person. One by calling the method “create” and the second by
finding
the first record in the database. Again, object instances are not
pointers
to the database record. The objects “p” and “pp” are totally seperate.
Like
above, activating the “destroy” method sends a destroy to the database
based
on the id in the object but does not destroy the calling object or any
other
object that happens to have the same id value. The “save” method
returning
true is strange since the record with the id value of 1 can’t be created
again or updated.

Someone else might know more as to why “save” returns true in this case.
If
not, then it is most likely a bug.

B.

I am posting a related question in the same thread.
It is more of a philosophical question.
Can anybody please give me some philosophical explanation why the
following behavior of ActiveRecord is considered ok (or should i submit
a bug report/feature request?):

In console:

p = Person.create(:name=>‘Bill’)
p.destroy
p.name # => “Bill”
p.save # => true
Nothing is saved in the database, but what disturbs me more is that
“save” returned true in such case.

A more elaborate version:

p = Person.create(:name=>‘Bill’)
p.id # => 1
pp = Person.find(1)
pp.destroy
p.persisted? => true
p.destroyed? => false
p.name = “John”
p.save # => true
but the database is empty.
Again, what bothers me the most is the “true” returned by “save”.

Alexey.

On Apr 1, 6:50am, Michael P. [email protected] wrote:

execution as far as SQL is concerned.
Although you might expect rails to check the numbers of rows modified
(as it does when optimistic locking is enabled) - Select * from foo
where id =‘non existant’ will also execute just fine but rails chooses
to make Foo.find(id) raise an exception in those cases

Fred

On 1 April 2011 10:21, Frederick C. [email protected]
wrote:

UPDATE my_table SET field1 = ‘new value’ WHERE id =

You’ll get a message “no records were updated” - that’s a successful
execution as far as SQL is concerned.

Although you might expect rails to check the numbers of rows modified
(as it does when optimistic locking is enabled) - Select * from foo
where id =‘non existant’ will also execute just fine but rails chooses
to make Foo.find(id) raise an exception in those cases

I agree it probably breaks the principle of least surprise, so I’ve
just had a rummage in active_record\base.rb (Rails 2.3.11)

Obviously, .find returns rows, so it’s easy to raise an error if the
size of the returned array is nil, but I was curious about the return
value of create_or_update - so, when I try the previous example [1]:

p = Person.create(:name=>‘Bill’)
p.destroy
p.name # => “Bill”
p.save # => true

It works fine as the update method returns a true value if
quoted_attributes is empty (as it uses that to build the set of fields
to update - no fields to update == no query to run), but if you modify
the object before saving (eg, > p.name = “William”), it errors with a
“TypeError: can’t modify frozen hash” when trying to update the
updated_at value because it’s frozen the @attributes_cache collection
when destroy was called…

So yes, it probably would make more sense to have .save return false
or raise on saving a destroyed record - but it should raise if you try
to alter a destroyed record, and it makes not much sense to save a
record you’ve destroyed and not altered… :-/

I don’t know which way to plump… any thoughts?

[1] That example is terribly contrived, and it’s making it hard for me
to make up my mind if this behaviour is “bad” or not. Anyone got a
real-world example of where this is causing a problem?

Fred, example [1] was meant to be a minimal example.
I am more concerned about a situation when the same record is accessed
by two applications or through two objects.

I said that my question was philosophical (which does not mean that such
behavior is not a bug).
I understand why SQL executes “successfully”, but if .save is only meant
as a shortcut to a specific SQL, it should have been called differently,
and in fact it should have been simply replaced by two methods: .insert
and .update, which would return “true” if the SQL executed successfully,
and there i wouldn’t have been surprised.

The way it is, .save seems to hang awkwardly halfway between pure SQL
(but not quite there because it verifies if the object has been saved
before, which in many situation can be redundant, as the developer might
know this in advance) and something more intelligent that would return
“true” only if the object has been saved.

Any more comments, or should i try to submit a bug report?

Alexey.