In Rails, I would like to be able to have a model that checks if a value for a column signature already exists, if it does not, save it, if it does, update the existing model and then exit out. This has to be a model solution. I cannot use controller logic to achieve this, it has to be automatic.
I have tried using filters like before_create or before_save, but it doesn't seem like there is a clean way to stop the filter chain and update an existing record by those means. Any help is appreciated. Thanks.
The code below is for a method that you could use instead of save. It checks if a model exists with the same signature. If it finds one, it updates that existing model with the attributes and then return that model. If there is no other model that has that signature, it will continue on saving. You will have to use this in place of ActiveRecord's #save in places where you want this behavior. There was no way to do this using callbacks since the only way to cancel saving in a callback was to have it return false.
def save_signature
model = Model.find_by(signature: signature)
if model
model.update(attributes)
model
else
save
end
end
Let me know if I understood what you wanted. If not, then just point out what I misunderstood and I'd be happy to change my answer accordingly.
Maybe not the cleanest way but why to stop the chain?
fetch the object on before_* filter, do the check and update the model, it will be saved with the old value.
what do you think?
Read about find_or_create_by http://guides.rubyonrails.org/active_record_querying.html#find-or-create-by . This will either create a new record or output you the previous one. Hopefully it may work for you
Related
I'm curious how exactly those callbacks work. If i initialize an object, and then use method save to throw it into my database, will before_create callback work?
Similar thing with before_save. is it called literally only before function save is being used, or will it be triggered also in case of using create method?
before_save will be triggered before you save a record. It doesn't matter whether you're creating or updating a record, your callback will be triggered. So, yes, it will also be triggered when you use the create method.
before_create is only triggered before creating a record, not before updating a record.
There is also before_update, which is only triggered before updating, but not before creating.
This doesn't depend on which method you use, it depends on whether the record was persisted before or not. In other words, it depends on whether you're updating or creating a record.
http://api.rubyonrails.org/v4.1.1/classes/ActiveRecord/Callbacks.html this url will help you find right answer for you. On this url you can also find the sequence for call back. Hope it helps you.
The issue that my application is already alive and bunch of user's use data, in which I found bug and fixed it in the code. But how can I modify their data with edited method, which is actually is before_save callback?
I have their input for this record stored in another column, I need to modify output.
How I can do this?
Do you mind if the updated_at value of your model changes?
If not, I suppose you could loop through your model collection calling the save method.
> Foo.all.each(&:save)
If you go this way, you better stop your application before you make changes to the database.
I would like to always have my model look to see if an object exists before saving. And if it does, then not to create a new object, but to use an existing one.
Is it possible to add this directly to the before_save ActiveRecord class of my model ?
I believe this is impossible because you can not change the value of self.
ActiveRecord already provides this functionality. You can do something like:
User.find_or_initialize_by_email('some#email.com')
This will return the user from the database, if found, or a new user with the email set to the passed parameter. You can then use the object as you would otherwise and call save when you are done (which will update or create as needed).
More info on http://api.rubyonrails.org/classes/ActiveRecord/Base.html (search the page for find_or_initialize_by_)
I don't think you can add this to a before_save. If there are only a few places where you creating your object then you could manually specify find_or_create (or find_or_initialize) as opposed to just using create or new
If you always want it to happen, you could override the new method to behave like find_or_initialize
I've been wondering, suppose I have a model with an attribute that in every instance is dependent on that same attribute in other instances. The best example for this would be an order attribute for items in a list.
The best place to update the rest of the items' order attributes would be in a before_update callback method, were you have both the item's old and new values.
But now whenever you update the other items in the list the callback is going to be called again, and again...
I'm looking for an elegant way of solving this.
I have heard about the :update_without_callbacks method, but i don't want to use a private method, and also i feel like adding extra attributes would be unnecessary.
Got any good ideas? Thanks in advance!
One way would be to use update_all to set the order of all the other items in bulk.
That way you would efficiently limit the number of queries to one and prevent any callbacks from being triggered.
https://github.com/rails/rails/blob/83e42d52e37a33682fcac856330fd5d06e5a529c/activerecord/lib/active_record/relation.rb#L274
I feel the fact that you have to do this type of update across entries suggests you haven't properly conceptualized your problem. Why not create a List model that has the order attribute, and then create a one-to-many relationship between the List model and the Item model. This way, there's only one place to update the ordering information and no need for complicated and brittle callbacks.
I want to log the user who destroyed a record using before_destroy callback. But, I dont know how to pass arguments to before_destroy(and I am not sure if it is possible). Maybe I am dealing this in the wrong way. Any other perspective to do this will also do.
Create a virtual attribute on the Record model called destroyer or something.
Then you can do whatever you want with that attribute in your before_destroy callback.