Update if exists, else do nothing? - ruby-on-rails

I'm trying to update a value of a model if and only if the model exists. If it doesn't, I do nothing. Searching only seems to return update or create questions/answers, but I do not want to create.
I know I can do it with a simple:
found = Model.find_by_id(id)
if found
update stuff
end
However, I feel like there is a way to do this in one call, without assigning any temporary local values or doing an if.
How would I write a rails call to update a record without noisy errors if it doesnt exist?
Latest Rails 3.x

You can use try method of Rails before calling update_attributes on result of find_by_id or where.
try will return nil silently without raising exception if the record does not exist. If the record exists, it will update it.
found = Model.find_by_id(id).try(:update_attributes, {key: value})

You can use first_or_initialize combined with new_record? as following:
client = Client.where(first_name: 'Nick').first_or_initialize(locked: false)
client.save unless client.new_record?

Assuming your model is called 'Event' and you search by id, you could do:
e = Event.where(:id => id)
if !e.empty?
e.first.value = new_value
e.first.save!
end

In Rails 4, this is probably the simplest solution I found:
# POST /users
# POST /users.json
def create
#user = User.find_or_initialize_by(fbid: user_params[:fbid])
#user.assign_attributes(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render action: 'show', status: :created, location: #user }
else
format.html { render action: 'new' }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
If the user with FBID exists, then the record will be updated; otherwise, a new one will be created. You can update the record to match whatever column you want to keep unique. Probably indexing the column would help the search & retrieval etc.
Hope this helps!

Related

find results from table after update, where (attribute integer) is equal to the (primary id integer)

I am updating an object in a table with attributes : :primary_id, :zucode_number, :zucode_email
The updated row object does NOT have the attributes :zucode_number and :zucode_email .
Other different rows in the same table do have the attributes i need, :zucode_number and :zucode_email.
:zucode_number(integer) can be equal to, is sometimes equal to the :primary_key(integer)
How to get the rows where :zucode_number is equal to :primary_key and send email to :zucode_email on those rows.
Hope this makes sense...
Ive been, and i am, struggling with this, and can't get it to work. Thanks in advance for your help.
Asked another question similar yesterday but think it wasn't clearly explained.
I am updating (successfully) the object(zucode), sending it through a link to:
def changezu
zucode = Zucode.where( id: params[:id], secret2: params[:secret2] ).first
if zucode
respond_to do |format|
format.html { redirect_to edit_zucode_path, notice: 'Make changes!.' } #edit_zucode_path
format.json { head :no_content }
end
end
end
my update method is :
def update
respond_to do |format|
if #zucode.update(zucode_params)
format.html { redirect_to #zucode, notice: 'Zu was successfully updated.' }
format.json { render :show, status: :ok, location: #zucode }
else
format.html { render :edit }
format.json { render json: #zucode.errors, status: :unprocessable_entity }
end
end
end
UPDATE::
added to controller :.
after_action :trynew, only: [:update]
and
def trynew
#zucode = Zucode.where("zucode_number=id")
#ZuMailer.mymail.(zucode).deliver
end
with the mailer commented out, it does not give error but nothing happens. Looking into log the correct rows are not being selected or mentioned. I only see reference to the updated row. If i run with the mailer i get error for "wrong arguments 0 of 1"
As I can see, that you need to use callbacks of object lifecycyle.
I am assuming that you have a model called Zucode .
Inside that model add after_update method.
class Zucode
after_update :method_to_call
def method_to_call
zucodes = Zucode.where('zucode_number=id')
#perform action on zucodes
end
end
The method method_to_call will get called each time a Zucode is updated.

Ruby on Rails Update field of all users

I'm having a bit of trouble and I'm not sure if it's even possible to do what I want.
I have a user model with a field called points. I also have a matches model.
I've been trying to use my matches_controller to update the points field of every user when a match gets updated (by a site admin). The goal is to add points if the user selected the correct score.
I'm not sure if I'm able to do this. I'm wondering am I incorrect trying to access the user model from the matches_controller? Because I want to update all fields when a match score is updated, I need to do it from the matches_controller
I've been going around in circles trying to solve this. Am I approaching it incorrectly?
Thanks for reading and hopefully helping.
Here's the relevant code
matches_controller.rb
def update
respond_to do |format|
if #match.update(match_params)
format.html { redirect_to #match, notice: 'Match was successfully updated.' }
format.json { render :show, status: :ok, location: #match }
else
format.html { render :edit }
format.json { render json: #match.errors, status: :unprocessable_entity }
end
end
MatchPick.where(:matchID => params[:id]).update_all(result: match_params[:result], closed: match_params[:played], points: 0)
MatchPick.where(:matchID => params[:id], :userPick => match_params[:result]).update_all(points: 3)
update_user_points
end
def update_user_points
#users = User.all
#users.each do |user|
user.points = 4
puts params
end
end
You can't use update_all unless you're updating a field in all the records to the same value, which is not the case here.
How about this?
MatchPick.where(:matchID => params[:id], :userPick => match_params[:result]).each do |mp|
mp.user.update_attribute(:points, mp.user.points + 4)
end
(this is assuming that MatchPick is a join record between Match and User and has a belongs_to relationship with User)

Rails 4 - Delete rows from another table before updating

I have the following code in my members controller:
def update
#member.phone_numbers.destroy_all
respond_to do |format|
if #member.update(member_params)
format.html { redirect_to #member, notice: 'Member was successfully updated' }
else
format.html { render action: 'edit' }
end
end
end
I have to delete existing records from phone_numbers before updating members, because the phone numbers must be inserted again (because of possible ordering changes and other reasons, but it does not matter).
The question is: It works, but if members fail to update, all the phone numbers will be already deleted.
What could be done to avoid the problem if the #member.update fails?
You may consider marking the phone numbers for destruction instead of actually deleting them.
#member.phone_numbers.map(&:mark_for_destruction)
Then, when you do #member.update, it should do the update and the destruction of the associated phone numbers all at once. Here's the API for #mark_for_destruction: http://api.rubyonrails.org/classes/ActiveRecord/AutosaveAssociation.html#method-i-mark_for_destruction
Otherwise, you can look into setting up a transaction block. The API explains this well enough: http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html
Wrap this into a helper method that wraps the statements into a transaction and call from the controller:
# Member Model
def delete_phone_numbers_and_update(params)
Member.transaction do
phone_numbers.destroy_all
update(params)
end
end
# Controller
def update
respond_to do |format|
if #member.delete_phone_numbers_and_update(member_params)
format.html { redirect_to #member, notice: 'Member was successfully updated' }
else
format.html { render action: 'edit' }
end
end
end

Save referenced resource on update_attributes (create nested resource on edit)

I have something like issue tracking system where there are issues and they have some comments.
Now on one page I want to give user an option to edit some stuff of "issue" as well as add a comment. Editing of and issue is a standard stuff like in /edit but also I want to create a comment and validate if it's not blank.
I've figured out that I can build a comment and make a form for it, but how should I check simultaneously that both issue attributes and comment attributes are valid? Because each update should be followed by a new comment, but I don't want to create a new comment if the issue attributes are no valid.
I would approach this by first adding fails_validation? methods to both your Issues and Comments models to check for problems.
Second, you will have to manually load the #issue form data from params[] and validate it BEFORE you save it (can't use update_attributes(params[:issue]).) Create a new Comment and load it via params[]. Then you can test the validation on both models and go back to the edit action if either fails.
If both pass you can save #issue and then #comment as normal.
def update
#issue = Issue.find(params[:id])
# manually transfer form data to the issue model
#issue.title = params[:issue][:title]
#issue.body = params[:issue][:body]
#...
#comment = #issue.comments.new(params[:comment])
# validate both #issue and #comment
if #issue.fails_validation? || #comment.fails_validation?
flash[:error] = "Your edits or your comment did not pass validation."
render :action => "edit",
end
# validation passed, save #issue then #comment
respond_to do |format|
if #issue.save
#comment.save
format.html { redirect_to #issue, notice: 'Issue successfully updated. Comment created' }
format.json { head :ok }
else
format.html { render action: "edit" }
format.json { render json: #issue.errors, status: :unprocessable_entity }
end
end
end
Not the most elegant solution, but it should work.
You can validate the comment model and the issue model in their respective classes.
It is not clear to me whether you are using 'accepts_nested_attributes_for' in Issue for comments. If you are, then the standard IssueController#update will not save the record if issue is invalid and consequently, it will not create the comment records as well.
Here is the standard IssueController#update:
class IssueController < ApplicationController
def update
#issue = Issue.find(params[:id])
if #issue.update_attributes(params[:issue])
redirect_to issues_path, notice: 'issue updated'
else
render action: 'edit'
end
end

How do I call update action from another action in rails 3?

So I'm writing a basic member modifying action, and I figured, lets stay DRY and just modify the params hash then pass along to our update method but it doesn't seem to work. I guess there is some rails magic going on that I can't find... From what I've read this should work. I'm using Rails 3.2.
Here's an example of what I'm trying to do:
# POST /tasks/1/toggle_done
def toggle_done
#task = Task.find(params[:id])
puts "<<<<<", params
# invert done bool value
params[:done] = !(#task.done)
# thought maybe update_attributes retured a full set of
# attributes in the params...
#params[:name] = #task.name + "...test."
# thought maybe the method call to update was getting
# filtered or something. Doesn't seem to help.
#params[:_method] = "put"
# redirect to update with these new params
puts ">>>>>", params
# Why bother rewriting task.done = x; task.save;
# redirect_to show; etc when update already does that.
update
end
# PUT /tasks/1
# PUT /tasks/1.json
def update
#task = Task.find(params[:id])
puts "======", params
respond_to do |format|
if #task.update_attributes(params[:task])
format.html { redirect_to #task, notice: 'Task was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #task.errors, status: :unprocessable_entity }
end
end
end
I get the following console output:
<<<<<
{"_method"=>"post", "authenticity_token"=>"CVqzsJfSVgM7Bq/kXlrjzkWVoA7Pbne4GNEHqbQB42s=", "action"=>"toggle_done", "controller"=>"tasks", "id"=>"1"}
>>>>>
{"_method"=>"put", "authenticity_token"=>"CVqzsJfSVgM7Bq/kXlrjzkWVoA7Pbne4GNEHqbQB42s=", "action"=>"toggle_done", "controller"=>"tasks", "id"=>"1", "done"=>false, "name"=>"Put Done button in index view...test."}
======
{"_method"=>"put", "authenticity_token"=>"CVqzsJfSVgM7Bq/kXlrjzkWVoA7Pbne4GNEHqbQB42s=", "action"=>"toggle_done", "controller"=>"tasks", "id"=>"1", "done"=>false, "name"=>"Put Done button in index view...test."}
So it seems like the params array is set right. It renders the regular show view with the flash message "Task was successfully updated.", so it seems like the whole method gets executed but non of the model properties are getting changed. I guess something inside update_attributes is failing. Can anyone shed some light on this for me?
Also is this a crazy thing to do? Should I be setting and saving inside my toggle_done method instead of chaining to update?
Rails saves the attributes for the task object in the hash params[:task]. So you in your toggle_done method you need to save the result in params[:task][:done] otherwise rails cannot associate the done attribute with the task.
def toggle_done
#task = Task.find(params[:id])
params[:task] = { done: !(#task.done) }
update
end
But with calling the update method you make 3 database queries where only 2 are neccessary - And the first 2 are identically because you load the Task with the ID in the toggle_done method as well as in update.
To avoid this you can put the save and redirect part into a protected method and call it when you want to save it. Like this:
def toggle_done
#task = Task.find(params[:id])
params[:task] = { done: !(#task.done) }
save_updated
end
def update
#task = Task.find(params[:id])
save_updated
end
protected
def save_updated
respond_to do |format|
if #task.update_attributes(params[:task])
format.html { redirect_to #task, notice: 'Task was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #task.errors, status: :unprocessable_entity }
end
end
You're passing params[:task] to update_attributes, which doesn't exist. Try:
params[:task] = {:done => !(#task.done)}

Resources