This project is archived and is in readonly mode.
[PATCH] ActiveRecord#touch without validations
Reported by Drew | April 19th, 2009 @ 02:53 PM | in 3.0.2
The functionality for the new ActiveRecord#touch is not what I would expect: "If the save fails because of validation errors, an ActiveRecord::RecordInvalid exception is raised"
I feel that least-surprise would be the following:
.touch should not run the validations, but instead make the change no matter what ( just as if you called model.update_attribute(:updated_at, Time.now) )
.touch! should run the validations and raise an error if it fails ( just like .save! )
Comments and changes to this ticket
-
Drew April 19th, 2009 @ 02:54 PM
Per Pratik's suggestion, I have made a patch JUST to change the .touch to use save(false)
-
Mike Breen April 19th, 2009 @ 09:06 PM
This patch applies cleanly for me on edge. I also feel strongly that
touch
should not run the validations and there should be atouch!
for that case. -
Nathaniel Bibler April 20th, 2009 @ 02:53 PM
+1 for disregarding validations with
touch
. I also agree with Mike on providing atouch!
to run validations and raise save errors.My only thought on
touch
in general is that if we're mimicking the *NIXtouch
command, it seems odd that ourtouch
has the potential of updating more than just the theupdated_at
(or other given column). By internally callingsave
at all on the model, aren't we opening ourselves up to:Post = post.first post.title = 'Foo' post.touch post.reload post.title # => 'Foo'
It's probably just me, but that seems quite counter-intuitive, as well. In my mind,
touch
should only affect the time stamp field(s) and not permanently save other dirty data in the model. -
Drew Blas April 20th, 2009 @ 04:21 PM
That is definitely an issue. Pratik suggested using update_attribute instead. In that case, it's possible that there will be two database writes if the model has both an updated_at and updated_on field (which is probably a VERY VERY edge case).
I vote for changing touch to use update_attribute to avoid the side-affects of calling #save on a dirty object.
-
grosser September 2nd, 2009 @ 05:47 AM
While your at it, how about the class-level touch, so there is a matching set.
class ActiveRecord::Base def self.touch(ids) update_all({:updated_at=>Time.now.utc}, :id=>ids) end end
-
Josh Sharpe September 2nd, 2009 @ 04:40 PM
IMO any time save is called I want to know if my record is invalid so that I can do something about it. With this patch/idea it's not quite so obvious that touch doesn't call the validations, which can result in a propagation of invalid objects.
If you really want this functionality I'd suggest you alter #touch to accept false the same way #save does. Or define #touch! and change things accordingly.
-
Michael Hasenstein October 16th, 2009 @ 01:31 PM
+1
Also - touch(:other_column) always also updates :updated_at (Rails 2.3.4). Is it just me? This is not what it is supposed to do... but when I look at the code in "touch" it uses write_attribute, and I'm not deep into Rails' code (only using the API) but according to what I know and tried write_attribute doesn't cause a database write, there's a "save" somewhere else - which explains why :updated_at is updated. But this is completely the wrong behavior, even the current documentation says so!(?)
-
Conrad Taylor October 16th, 2009 @ 03:01 PM
+1
I was thinking that updated_at field should change anytime the object is modified
successfully whether a touch happens or not. It seems that the issue is that updated_at/on
has a special meaning in Rails to begin with and is causing a level of confusion with how
touch should work. The code as implemented considers two code paths:touch => updates updated_at and/or updated_on if they exist
touch(:attribute) => updates attribute and implicitly updates updated_at/updated_on ifthey exist
I agree that there should be touch! running validations and generate appropriate exception.
Also, there should be way to not overwrite fields that are not of the appropriate type when
using touch(:attribute). Maybe, this type of checking is more appropriate for the touch!(:attribute)
form and not touch(:attribute). -
Michael Hasenstein October 16th, 2009 @ 03:15 PM
Background - why updated_at should not be updated when "touch" has a column as parameter:
People like me use special "something_updated_at" columns for aiding in automatic cache expiration, using modelinstance.something_updated_at.to_s(:number) as one of the arguments in calls to "cache". That's what ActiveRecord does with a model instances cache_key method, but that method always uses updated_at. If I were to always use updated_at of an "Item" even though only its "comments" collection has been updated I'd needlessly expire certain cached fragments. I could just use the updated_at of the particular associated object - but my something_updated_at columns are the equivalent of counter_cache columns.
So I think there are (at least) two very different uses of timestamps:
a) for data consistency, as part of the business logic (when did you place this sorder?)
b) for the purely internal purpose of cachingWe have to keep that in mind when we argue - those thinking about the business logic will always vote for validations and the current behavior, those concerned with much lower-level internal code issues are going to vote for what's been said here.
Maybe there even is a legitimate need for different kinds of timestamp columns then? Okay, maybe not yet I guess.
-
Mitchell Hashimoto November 3rd, 2009 @ 05:29 PM
+1 Can this get merged in please? This just bit me in the butt and I think this makes the most sense.
-
Rizwan Reza January 23rd, 2010 @ 11:19 PM
- State changed from new to verified
- Tag set to 3.0, activerecord, feature, patch, touch, verified
- Assigned user set to Pratik
- Milestone cleared.
I have verified this patch and it applies cleanly to master.
+1 on the feature addition, this makes much more sense.
I talked to Obie over on IRC, he also thinks this should be in for Rails 3.0.
-
Rizwan Reza January 23rd, 2010 @ 11:21 PM
- Title changed from Patch - ActiveRecord#touch without validations to [PATCH] ActiveRecord#touch without validations
-
Rizwan Reza January 23rd, 2010 @ 11:50 PM
By the way, the above patch, while applying cleanly, gave deprecation messages... so here's the updated patch.
-
Obie January 24th, 2010 @ 12:29 AM
New patch taking comment thread feedback into account. Didn't add a class-level touch method, cause not sure how useful it is.
-
Pratik January 24th, 2010 @ 09:59 AM
- Assigned user changed from Pratik to DHH
-
Nathaniel Bibler January 24th, 2010 @ 04:00 PM
Seems to me as though Obie's patch is pretty close to the conversation, but I believe it ultimately provides conflicting functionality. If one uses
touch
to update a dirty model, all that is stored is the touched column, the other dirty attributes remain dirty and unsaved. However,touch!
, due to its use ofsave!
would not only update the touched column, but any other dirty attribute in the model will be stored, as well.So, the result of using the two methods,
touch
vstouch!
in this implementation has a conflicting result. On one hand if you end up with amodel.changed? #=> true
(withtouch
) while on the other you'll getmodel.changed? #=> false
.Would it be more appropriate to implement
touch!
as:def touch! raise ActiveRecord::RecordInvalid unless valid? touch end
Or something very similar?
-
Nathaniel Bibler January 24th, 2010 @ 06:09 PM
Patch integrating Obie's submission with a thinner implementation of touch!, utilizing his time stamp tests.
-
Pratik January 24th, 2010 @ 11:02 PM
Thinking about this again, I don't like the the fact that touch/touch! aren't equivalent of save/save!, as save and save! both run the validations. So we have two options here :
1) I like Josh Sharpe's suggest to have touch accept false ( which is now :validate => false ). So touch() and touch!() would both run the validations, touch!() would raise an exception on validation failures, while one can use touch(:validate => false) to bypass the validations.
2) Just use update_attribute() for touch() and not add touch! at all.
-
Repository March 27th, 2010 @ 07:35 AM
- State changed from verified to resolved
(from [3a875e618487a06a56f6cf912cf5440f294921cc]) Changed behavior of touch and added touch! Originally implemented by Obie Fernandez, updated touch! to act as a thin wrapper to touch. [#2520 state:resolved]
Signed-off-by: wycats wycats@gmail.com
http://github.com/rails/rails/commit/3a875e618487a06a56f6cf912cf544... -
Repository March 27th, 2010 @ 08:22 AM
(from [68ade93cde3fcb3ac9fdfe0541d33d22c2c748d7]) Revert "Changed behavior of touch and added touch! Originally implemented by Obie Fernandez, updated touch! to act as a thin wrapper to touch. [#2520 state:resolved]"
This reverts commit 3a875e618487a06a56f6cf912cf5440f294921cc.
http://github.com/rails/rails/commit/68ade93cde3fcb3ac9fdfe0541d33d... -
Yehuda Katz (wycats) March 27th, 2010 @ 08:25 AM
- State changed from resolved to incomplete
-
Pratik March 27th, 2010 @ 08:33 AM
I think touch and touch! should both run validations and touch! should raise an exception on validation failure. For bypassing validations, we need to add :validate => false argument to touch() - this way touch() and save() are more aligned.
-
Pratik March 27th, 2010 @ 08:33 AM
- Assigned user changed from DHH to Pratik
-
Michael Hasenstein March 27th, 2010 @ 09:21 AM
Apart from validations, I would like to re-raise the issue I posted above once more.
I have several "updated_at" columns for different aspects, to aid in cache-key generation. Those special "SOMETHING_updated_at" columns track the state of associated objects, while pure updated_at tracks the state of the object itself.
When I "touch" one of those associated time-columns the last thing I want is that the object's updated_at is updated! When I say "touch(:items_updated_at)" I want to tell my cache-key generation code that the user's associated items have changed. The user item itself did not change. If the user's updated_at column is changed by "touch" it would indicate that the user object itself changed - which is not true.
See what I wrote above.
I consider my use of timestamp-columns wholly natural. As I wrote above, this is "business logic" vs. "implementation details" - but we HAVE TO do this. An AR object often contains columns for the real application logic, but also columns that are used internally only. If I don't cache associated timestamps with the user (example) I have to do a SQL JOIN each time to load all associated objects and find out if anything changed!
So PLEASE help me and provide a method that lets me update my timestamps WITHOUT also updating "updated_at" of the record in question, if I specify a different column to use by "touch". I would not ask for it if I wasn't convinced my "use case" is completely regular and not an "edge case".
-
Dan Pickett May 15th, 2010 @ 02:11 AM
Pratik, what's actionable here? If bugmashers can help, please feel free to tag this with 'bugmash'
-
Jeremy Kemper May 15th, 2010 @ 05:59 PM
-1
it should raise, because is not meant to act as save for other changes you made.
Best would be to call the class method suggested which only updates this single field and no others.
-
Rizwan Reza May 15th, 2010 @ 06:01 PM
- State changed from incomplete to wontfix
-
Jeremy Kemper May 15th, 2010 @ 06:08 PM
- State changed from wontfix to incomplete
To be clear, +1 on grosser's suggestion of a class method that touches the record without saving other attributes. And revert the
touch!
method. -
Rizwan Reza May 15th, 2010 @ 06:17 PM
- Tag changed from 3.0, activerecord, feature, patch, touch, verified to 3.0, activerecord, bugmash, feature, patch, touch, verified
-
José Valim June 29th, 2010 @ 05:49 PM
- Importance changed from to Medium
Hey guys, anyone in mood to work on a patch?
-
Neeraj Singh June 30th, 2010 @ 12:24 PM
- Assigned user changed from Pratik to José Valim
- State changed from incomplete to open
attaching a patch which fixes update_attribute. This patch does not fix the issue with this ticket but getting there.
-
Edgars Beigarts June 30th, 2010 @ 07:42 PM
Shouldn't the last patch use
primary_key = self.class.primary_key self.class.update_all({ name => value }, {primary_key => self[primary_key]})
instead of:
self.class.update_all({ name => value }, {:id => self.id})
?
-
José Valim June 30th, 2010 @ 07:49 PM
I think :id automatically is converted to primary_key, but that needs to be checked.
-
Neeraj Singh June 30th, 2010 @ 07:53 PM
did not work. and that is not good. I see :id at a few places. tonight I will spend some time looking at if I could fix the root cause. If not I'll implement the fix provided by Edgars. Edgars thanks for catching that.
-
Neeraj Singh July 1st, 2010 @ 03:48 AM
Attached is modified patch based on feedback provided by Edgars.
-
José Valim July 2nd, 2010 @ 06:49 AM
Hey Neeraj, thanks for the patch! Initially, we agreed upon update_attribute not updating updated_at or updated_on, but there is already a lot of people relying on this behavior for important stuff like touch. So it would be interesting if we don't lose this behavior. Could you please update your patch? Thanks! :)
-
Neeraj Singh July 4th, 2010 @ 04:37 AM
Attached is modified patch.
Please note that this patch depends on this commit http://github.com/neerajdotname/rails/commit/0db04b94b095b27f6715c0... .
-
José Valim July 8th, 2010 @ 10:01 PM
Great Neeraj! Both patches were applied, now waiting for a fix for touch! :) Thanks!
-
Repository July 13th, 2010 @ 07:37 AM
- State changed from open to resolved
(from [1d45ea081493cd4eca95cd75cce7be7b8d9cb07c]) with this fix touch method - does not call validations - doest not call callbacks - updates updated_at/on along with attribute if attribute is provided - marks udpated_at/on and attribute as NOT changed
[#2520 state:resolved]
Signed-off-by: José Valim jose.valim@gmail.com
http://github.com/rails/rails/commit/1d45ea081493cd4eca95cd75cce7be...
Create your profile
Help contribute to this project by taking a few moments to create your personal profile. Create your profile »
<h2 style="font-size: 14px">Tickets have moved to Github</h2>
The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>
People watching this ticket
Attachments
Referenced by
- 2520 [PATCH] ActiveRecord#touch without validations [#2520 state:resolved]
- 2520 [PATCH] ActiveRecord#touch without validations (from [3a875e618487a06a56f6cf912cf5440f294921cc]) Changed...
- 2520 [PATCH] ActiveRecord#touch without validations (from [68ade93cde3fcb3ac9fdfe0541d33d22c2c748d7]) Revert ...