Only update records that haven't been updated - ruby-on-rails

When creating a record i know you can use the method
.first_or_create!
to create records that don't already exist in the model. I need to do the same for when updating a model. I have an app that runs a rake task to apply a score to a column in my model.
prediction.update_attributes!(score: score)
I only want to update the scores that have not been updated yet.
is this possible?
Thanks

I think you might be looking for the try method which will attempt to call a method on an object that is potentially nil.
Example:
>> prediction.try(:update_attributes!, :score => some_new_score)
If prediction is nil it will just return nil, not throw a NoMethodError. If prediction is an object representing an existing record, then it will call the method on the object and update its score attribute.
http://api.rubyonrails.org/classes/Object.html#method-i-try

I agree with juanpastas that Rails will only save to the db if something has actually changed. IF you want to be more explicit in your code, Why not use the '.changed?' flag to save only dirty records? Look here for more details.

Related

Updating if exist or create if not rails

So im using an api to get info on weather, its executes everyday, what im trying to do is to get updated if already exist, and create a new one if it doesn't in table.
I do want to update all attributs when udpdating.
i did try
model = Model.where(column_name: value).first_or_initialize(locked: false)
but i get an error saying :
unknown attribute locked for Model
raise UnknownAttributeError.new(self ,k.to_s)
If you need anything, ask and i will comment or edit. Im newb to ruby and rails
Firstly, the model.Model part should be just Model, as Model is your class.
locked is supposed to be a column/attribute of the Model class, although it seems is not the case judging from your error. Therefore, I'm gonna use other_column_name as an example.
Explanation of what this is doing:
Model.where(column_name: value).first_or_initialize(other_column_name: some_value)
Model.where(column_name: value): gets models that satisfy the condition column_name == value
first_or_initialize: if a model such that column_name == value was found, that one is returned. Otherwise, it initializes a model with column_name = value.
By passing other_column_name: some_value, if the model was not found and needs to be initialized, it sets other_column_name to some_value but: 1) it does not update it if it was initially found and 2) it does not save the record.
The equivalent of first_or_initialize that saves the new record would be first_or_create but this would still not update the record if it already existed.
So, you should do something like this:
m = Model.where(column_name: value).first_or_initialize
m.other_column_name = some_value
m.save
This way, you first get a model where column_name is value or initialize a new one with this value if it didn't already exist. Then, you set the attribute other_column_name to some_value and save the model.
A one-liner alternative would be
Model.where(column_name: value).first_or_create.update(other_column_name: some_value)
However, note that if it needs to be created, this one will perform 2 queries (the insert and the update).
About the error part. It says the attribute locked does not exist on the Model record. Are these classes you created? Are you using some pre-existing project? You could try posting Model.attribute_names and maybe your schema.rb
Firstly refer to the docs here
A table by the name of weather with the following attributes location: string temperature:integer wind:string needing to be updated or initialized based on the location would work like this
#weather_record = Weather.find_or_initialize_by(location: location_value)
#weather.temperature = -60
#weather.wind = strong
#weather.save
Next, never, ever use a reserved name for a model so do not have Model as the name of your table
Lastly in your example
model.Model.where(column_name: value).first_or_initialize(locked: false)
you are saying
a_record.ClassName.where which is just wrong, If you are using a class method then start with the class name e.g. Weather.where if you are using instance methods then use the instance name e.g. an_instance_of_weather.some_field
Try this mate:
column_name_value = (Way that you get the info from data)
model = Model.find_or_initialize_by column_name: column_name_value
Let me know if worked!

How to mark as outdated an ActiveRecord object attribute?

I have a database trigger that modifies a field on INSERT. Then when I run object.my_attribute it returns nil instead of lets say 42.
If I do object.reload.my_attribute, this is fine. But I don't want to reload the whole object or part of it unless it is necessary. And I believe code shouldn't be concerned when and how an object was created. It should just be correct.
Is it possible to mark that particular attribute as outdated and any attempt to get its value to result in a query that fetches it from database?
For example:
after_save :forget_my_attribute
def forget_my_attribute
forget_field :my_attribute
end
I think it's better to make some service object where field is modified and call it when create the record. CreateModel.call(args) instead of Model.create(args). It will be more clear than database trigger I think
But you can do something like this
after_create_commit :fetch_my_attribute
def fetch_my_attribute
self[:my_attribute] = self.class.find_by(id: id)[:my_attribute]
end
Or more flexible fetch attribute you need dynamically
def fetch_attribute(atr)
self[atr] = self.class.find_by(id: id)[atr]
end
object.fetch_attribute(:my_attribute)

Postgres/Rails => is object guaranteed to be persisted if I do .save?

If I have a model and save it like this:
model = Website.new
model.attr = 1
model.id = 1
model.save #assume no errors in saving
then retrieve it like this:
model2 = Website.find(1)
Will model2 always be returned? Ignoring errors saving to the database.
Is there a possible scenario where the data is not yet committed to the database, and as a result the find results in no records found? Do I need to delay the find to guarantee the row is returned?
Assuming no database errors, and assuming you haven't overwritten save on Website, the only race condition you'd have is if you try to access the object (via find or otherwise) in the milliseconds before the record is created in the database.
So, to directly answer your question - yes, it's possible - but given a single database (e.g. no read-only slaves or anything like that), it's highly, highly unlikely.

Update serialized attribute without callbacks in Rails

I am trying to update a serialized attribute with some data in an after_save callback on the same object.
I don't want any callbacks to be triggered, for various reasons (side-effects, infinite loop). The typical way to achieve this would be to use update_column, but unfortunately that doesn't work with serialized attributes.
I am aware that I could put conditionals on my callbacks to avoid them getting called again, but it feels that there should be a form of update_attribute which doesn't trigger callbacks, but still works with serialized attributes.
Any suggestions?
This is what I do
serialize :properties, Hash
def update_property(name, value)
self.properties[name] = value
update_column(:properties, properties)
end
Sharing an example below how you can update serialize attribute without callbacks.
Suppose you have a train object, and there is a serialize attribute in that table called: "running_weekdays", that store on which day that particular train runs.
train = Train.last
train.running_weekdays
=> {"Mon"=>"true", "Tues"=>"true", "Wedn"=>"true", "Thur"=>"true", "Frid"=>"true", "Sat"=>"true", "Sun"=>"true"}
Now suppose you want to set the value for all weekdays as false except 'Monday'
changed_weekdays = {"Mon"=>"true", "Tues"=>"false", "Wedn"=>"false", "Thur"=>"false", "Frid"=>"false", "Sat"=>"false", "Sun"=>"false"}
Now you can update this by using update_column as below:
train.update_column(:running_weekdays, train.class.serialized_attributes['running_weekdays'].dump(changed_weekdays))
Hope this will help.
You need to code the hash before updating the column:
update_column(:properties, self.class.serialized_attributes['properties'].dump(properties))

Whats the most efficient way to continuously update and track the score of an object?

A class of objects, Item, has a score attached to it based on its attributes. I need to apply the calculate_ranking method to the Item class continuously (ideally when any of an object's attributes are changed) and efficiently to calculate this score:
Item.all.each do |i|
Item.update_counters i.id, :score_count => i.calculate_ranking
end
I've tried using attr_accessor :score(after adding a score column to the Item table) and after_initialize :calculate_ranking however it didnt work. Would be it possible to apply a custom counter cache to achieve this even though 'Score' isnt a separate model of its own? That is, without including: Class Score; counter_cache => true; end.
I'm not sure to understand you issue quite well. However take a look at callbacks here: http://guides.rubyonrails.org/active_record_validations_callbacks.html
and observers here:
http://guides.rubyonrails.org/active_record_validations_callbacks.html#observers

Resources