Rails Override Save to perform selective updates

I am working on a Rails 3.2.8 application that uses the built-in REST API to insert new data. The insertion logic is common to each endpoint and is obtained quite simply by calling Model.save.

For one type of model, I would like to first check if a pre-existing record exists, and if so, update, not insert. If the code allowed me to interact at the controller level, that would be easy with find_or_create_by, however (I think) my only option is to override the save method in the model or use the before_save callback.

I'm struggling to find a way to make this work, since any save or update_attribute calls inside the model simply lead to an infinite loop (for obvious reasons).

Is there a way to use either before_save or override the save so that I can first check if there is an entry with attributes x and y, and if so, get this entry and perform an update, otherwise go ahead with the standard Save ActiveRecord?

Here is my code as it currently stands inside the Activity model, which is not working due to an infinite loop problem:

def save a = UserActivity.find_or_initialize_by_user_id_and_activity_id(user_id: user_id, activity_id: activity_id) a.update_attributes start_at: start_at, end_at: end_at..... end 
+6
source share
1 answer

You seem to need the find_or_create_by_* method.

To avoid a loop, you should not place it in the save method, but in one of these two places:

Option 1: Controller Level

In the controller where you create the UserActivity instance, instead you write:

 a = UserActivity.find_or_create_by_user_id_and_activity_id(user_id: user_id, activity_id: activity_id) a.update_attributes start_at: start_at, end_at: end_at..... 

Option 2: class method

If you find that you are adding the above code to several contrllers, the best way would be to define a new class method in UserActivity :

 class UserActivity def self.create_or_update_from_attrs(user_id, activity_id, start_at, end_at...) a = UserActivity.find_or_create_by_user_id_and_activity_id(user_id: user_id, activity_id: activity_id) a.update_attributes start_at: start_at, end_at: end_at..... end end 

And in controllers, obviously:

 UserActivity.create_or_update_from_attrs(...) 

Replace save

Of course, you can override the save method, but this duplicates the functionality of Rails ( find_or_create_by... ) and, as such, violates DRY and , you can shoot yourself with your foot some time later when it contradicts any other situation that you encounter therefore I do not recommend using this:

EDIT: updated to avoid endless loop

 class UserActivity def save # If this is new record, check for existing and update that instead: if new_record? && a = UserActivity.where(user_id: user_id, activity_id: activity_id).first a.update_attributes start_at: start_at, end_at: end_at ... return true # just to comply with Rails conventions else # just call super to save this record super end end end 
+7
source

All Articles