Updating if exist or create if not rails - ruby-on-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!

Related

Is it possible to update an entire record using an object in rails?

I was wondering if I could update a record (entire row) given a replacement activerecord object.
Something like
Car.find_by(number: 1) = replacement_information_for_car_1
where replacement_information_for_car_1 is a Car activerecord object that I would like to use to replace the old record currently on the table.
You could do something like:
attributes = replacement_information_for_car_1.attributes
attributes.delete('id') # and anything else you don't want/can't be copied
Car.find_by(number: 1).update(attributes)
Not the cutest thing in the world, but it should do the trick.
The thing about ActiveRecord objects is that they have a unique identifier that you don't want to override. You'll have to assign the attributes without the id, that is most likely to be nil in your example.
Car.find_by(number: 1).update(replacement_information_for_car_1.attributes.except(:id))
That's a long line that can be refactored to something like
new_attributes = replacement_information_for_car_1.attributes.except(:id)
Car.find_by(number: 1).update(new_attributes)

Check that record doesn't exist/retrieve existing record active record

This question has two parts:
Consider an active record relation that has a Student - name:string, favorite_class:references and FavoriteClass name:string, abbrev:string.
First question: When creating a student record and adding a favorite class (name and abbrev) I want to check if that name/abbrev combo exists and if it does load that one for this association, else create a new one.
Second: When I try to update (Put) the student I want to be able to pass the abbrev of the favorite class and look up the record by that portion (assuming abbrevs are unique) and use that, else fail.
I am not seeing the rails way of doing this type of operation.
For the first question, the trick is to use find_or_create method:
#in the place where you create a new Student
#this method return first entity from db, if it doesn't found, will create one
fav_class = FavoriteClass.find_or_create(name: some_name, abbrev: some_abbrev)
Student.create(name: some_student_name, favorite_class: fav_class)
something similar you can do for your second question. Please, give me more details about it.
Update 1
If you want to update student's favourite class, you can do it in this way:
#I assume that you use latest vertion of rails of not, use .first instead of .take
new_class = FavoriteClass.where(abbrev: some_abbrev).take
student.update_attributes(favorite_class: new_class) if new_class

Only update records that haven't been updated

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.

rails 3, how can a reference to fieldname pull data from another source if field is empty?

In my app, users have one "template" record (in Template table) that sets defaults for their data.
Then they create multiple records in (Userdata table).
For each Userdata record, if they enter data into a field, the app uses THAT data (of course). But if Userdata.foo is empty I'd like to use Template.foo instead, transparently. And if both are empty, then it's "empty".
I'm pretty sure the right answer is NOT to code every single place I use every field:
if Userdata.foo.blank?
Template.foo
endif
And I assume it's a matter of somehow defining my model to redefine the fieldname somehow?
And I'm hoping there's some way to not even have to code the model method field-by-field, to basically say "if the field in UserDayta is blank, use the one in Template instead..."
You can define a method in your User model like so:
def fetch_attribute(att)
if self.userdata[att].nil? and self.template[att].nil?
return nil
elsif self.userdata[att].nil?
return self.template[att]
else
return self.userdata[att]
end
end

How to change type of a record and run validations of the correct class in rails using the type column?

I have a model which uses Single table inheritance and has different types (school year of type either semester or quarter) and each type has its own validations. If a user tries to change the type of the record, he can select which academic year type it is from a drop down and make changes. however, if the user changes the type, i cannot figure out how to make new class validations run and not old validations. For instance, my code is as follows:
#school_year = SchoolYear.find(params[:id])
respond_to do |format|
if SchoolYear::SUBCLASSES.include? params[:school_year]['type']
#school_year[:type] = params[:school_year]['type']
else
raise "Invalid type"
end
if #school_year.update_attributes(params[:school_year])
# done
else
# validation problem?
end
now if the year's type was semester, and the user changes it to quarter, i expect the QuarterSchoolYear's validations to run and not of those of semester. How do i make changes to code to make it work?
I guess you should reload the object after you change the type. Just assigning a new value to the 'type' attribute will not change the ruby class of the object. Of course, when you save the object just after the type change, the old validations will be used.
You may try to update the type attribute in the database, and then load the object.
Something like:
[..]
if type_differs_and_is_acceptable_to_change
SchoolYear.update_all(
['type = ?', params[:shool_year][:type] ],
['id = ?',#school_year.id ]
)
#school_year = SchoolYear.find(#school_year.id)
end
if #school_year.update_attributes...
Be sure to have :type NOT in attr_accessible for this class.
Besides that, I find it a little disturbing to change the type of the object, but that may be just my personal fear. :)
This is old, but cleaning up and improving upon #Arsen7's suggestion, the best way to reload your object as its new type is to use the following ActiveRecord method (which is made for this very reason), and it doesn't require you to save the object after changing its type. This way, you're not hitting the database twice:
[..]
if type_differs_and_is_acceptable_to_change
#school_year.type = params[:school_year][:type]
#school_year = #school_year.becomes(#school_year.type.constantize)
end
if #school_year.update_attributes...
You need to instantiate with the right type of model to run the validations. I don't see a way to do this except to save with the new type and find it again:
new_type = params[:school_year]['type']
#school_year[:type] = new_type
#school_year.save!
#school_year = new_type.constantize.find(#school_year.id)
#school_year.update_attributes(params[:school_year])
You'd probably want to put this in a transaction so you could rollback the change of type if update_attributes fails.

Resources