Ruby on Rails Update saving to wrong ID - ruby-on-rails

I have this site where a user can have many pets. These pets are listed on there profile, and clicking on a pet takes them to that pets profile, where they have the option to edit the pet. Loading the form for the pets works great, it gets the correct information for each one, but then there's the problem with updating. Clicking submit on the edit form causes all updates to go to the first pet they have. Like, if the have the pets with the IDs 2, 5 and 11, and updates on 5 or 11 go to 2. I'm not quite sure what's going on.
Here are my edit and update actions for the PetsController:
#GET to /users/:user_id/pet/edit
def edit
#user = User.find( params[:user_id] )
#pet = Pet.find_by_id( params[:id] )
end
#PUT to /users/:user_id/pet/
def update
#Retrieve user from database
#user = User.find( params[:user_id] )
#Retrieve user's pet
#pet = #user.pets.find_by( params[:id] )
#Mass assign edited pet attributes and save (update)
if #pet.update_attributes(pet_params)
flash[:success] = "Pet updated!"
#Redirect user to profile page
redirect_to user_path(id: params[:user_id] )
else
render action: :edit
Rails.logger.info(#pet.errors.messages.inspect)
end
end
I've tried different ways of finding the users pet, such as
#user.pet(params[:id])
That resulted in an error of it saying it couldn't find a pet without an ID, so next I tried
#user.pet.find_by_id(params[:id])
But that resulted in update_attribute not being defined, so I assumed I shouldn't be using find_by_id anyways
Is there another way I should be doing this perhaps? I'm thinking the error is that the pet's ID isn't being passed in correctly. If so, what could be a fix for that? I tried passing the ID as a hidden field from the edit form, but that didn't work.
Here's what happens in the terminal when a user tries to update a pet
Processing by PetsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"0tC+qRK01CMCEZWbDtJyZ4QNmXQxnaAPKT+anRKGKh5hTWekTbEHzAd+8 +YOVIWBB0/imVrMVNcpkeBR18SHNw==", "pet"=>{"color"=>"Darigan", "species"=>"Yurble", "gender"=>"Male", "level"=>"25", "hp"=>"50", "strength"=>"50", "defence"=>"50", "movement"=>"50", "uc"=>"1", "rw"=>"0", "rn"=>"0", "uft"=>"1", "ufa"=>"0", "description"=>"uiy7i77i7i", "id"=>"5", "name"=>"Fouany", "hsd"=>"150"}, "user_id"=>"5"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 5], ["LIMIT", 1]]
Pet Load (0.3ms) SELECT "pets".* FROM "pets" WHERE "pets"."user_id" = ? LIMIT ? [["user_id", 5], ["LIMIT", 1]]
(0.1ms) begin transaction
Pet Exists (0.2ms) SELECT 1 AS one FROM "pets" WHERE "pets"."name" = ? AND ("pets"."id" != ?) LIMIT ? [["name", "Fouany"], ["id", 2], ["LIMIT", 1]]
(0.1ms) rollback transaction
Rendering pets/edit.html.erb within layouts/application
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 5], ["LIMIT", 1]]
Rendered pets/edit.html.erb within layouts/application (12.4ms)
Profile Load (0.4ms) SELECT "profiles".* FROM "profiles" WHERE "profiles"."user_id" = ? LIMIT ? [["user_id", 5], ["LIMIT", 1]]
{:name=>["has already been taken"], :color=>[], :species=>[], :gender=>[], :level=>[], :hp=>[], :strength=>[], :defence=>[], :movement=>[], :uc=>[], :rw=>[], :rn=>[], :uft=>[], :ufa=>[], :description=>[]}
Completed 200 OK in 202ms (Views: 136.1ms | ActiveRecord: 5.0ms)
As you can see, the pet's ID goes from 5 to 2 back from 5, so maybe that's somewhere to start for fixing the issue?
Any suggestions and advice is greatly appreciated!

#user.pets.find_by(params[:id])
Should be
#user.pets.find_by(id: params[:id])

If your user should have only one pet (the route suggests this), then the pet id is not needed to access it. You can get the pet by
#user = User.find( params[:user_id] )
#pet = #user.pet
Have a look at this comment in your code:
#PUT to /users/:user_id/pet/
It mentions the user_id, but not the id

Related

Confusion in object ID when editing and deleting

Specify the ID of the desired object, while the first object of the model is always deleted/edited. For the first time I met such a problem, checked everything. What's wrong?
Started DELETE "/questions/6" for 127.0.0.1 at 2018-07-05 10:48:13 +0300
Processing by QuestionsController#destroy as HTML
Parameters: {"authenticity_token"=>"luh7ShhQ9pWka7wmWMnG4WMQVAnKRjtAJwn0s5at8/GBDOtFjUwZEF70o8hOFOaAN+pVB592V1+egH/PDJVUVA==", "id"=>"6"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 1], ["LIMIT", 1]]
↳ /Users/gorbunov/.rvm/gems/ruby-2.4.1/gems/activerecord-5.2.0/lib/active_record/log_subscriber.rb:98
Question Load (0.1ms) SELECT "questions".* FROM "questions" WHERE (6) LIMIT ? [["LIMIT", 1]]
↳ app/controllers/questions_controller.rb:86
(0.0ms) begin transaction
↳ app/controllers/questions_controller.rb:45
Question Destroy (0.4ms) DELETE FROM "questions" WHERE "questions"."id" = ? [["id", 3]]
↳ app/controllers/questions_controller.rb:45
(1.6ms) commit transaction
↳ app/controllers/questions_controller.rb:45
Redirected to http://localhost:3000/questions
Completed 302 Found in 7ms (ActiveRecord: 2.4ms)
I want to delete an object with ID =6, but delete an object with ID = 3
Action Destroy in controller:
def destroy
#question = Question.find_by(params[:id])
#question.destroy
flash[:success] = 'Вопрос успешно удалён!'
redirect_to questions_path
end
Link_to helper for delete object:
<%= link_to qest, class: 'btn btn-outline-danger', method: :delete, data: {confirm: "Хорошо подумал?"} do %>
You are using find_by without mentioning the column. When used without specifying column, find_by uses it as WHERE condition
Change it to Question.find_by(id: params[:id])(returns nil if matching record is not found) or Question.find(params[:id])(raises ActiveRecord::RecordNotFound error if record not found).
This is the clue.
Question Load (0.1ms) SELECT "questions".* FROM "questions" WHERE (6) LIMIT ? [["LIMIT", 1]]
↳ app/controllers/questions_controller.rb:86
This is generated from #question = Question.find_by(params[:id]). Note the WHERE (6). find_by takes either a single argument of raw SQL to use in the where clause, or a column and a value. You've only given it a single argument, so it's interpreted it as SQL and thus WHERE (6). This will be true for every column, so you get some random column, in this case 3, then you destroy it.
Instead you want:
#question = Question.find_by(id: params[:id])
Or better, use find.
#question = Question.find(params[:id])
And if you're just going to destroy it, use destroy on the class.
Question.destroy(params[:id])

Rails. Debugging a Uniqueness validation with scope that fails when object is updated

I have a model Prediction for which its instances have references to other models Participant and Match through the attributes :participant_id :match_id.
The idea is that a #participant can have only one #prediction per #match (every #participant is allowed to give maximum one prediction per #match).
So I've included the following validation in my Prediction model
validates :match_id, uniqueness: { scope: [:participant_id] }
The validation works OK to disallow #prediction objects to be created when there's already a #prediciton for the same #match and #participant. The problem is that it does, as well, prevent updating an object even though I'm not changing those assigned ids of the original object. Plus, it's letting me update only one instance of Prediction, all other's through an error.
What's puzzling is that it was working fine but for some reason, I've been unable to track, it stopped working and I have run out of ideas on how to debug. Could you provide any ideas? Below what I've checked/tried:
The relevant params of a failed request:
"prediction"=><ActionController::Parameters {"participant_id"=>"1", "match_id"=>"2", "local_score"=>"2", "visitant_score"=>"0"}
The error I'm getting:
#details={:match_id=>[{:error=>:taken, :value=>2}]},
#messages={:match_id=>["has already been taken"]}>
There are plenty predictions in the database with match_id: 2. However, this is happening despite having no other #prediction in the database with both match_id: 2 and participant_id: 1 except, of course, for the instance that throws this error while updating.
Funnily enough, if I try the same operation in the console (as below) it successfully updates.
#prediction = Prediction.find_by(participant_id: 1, match_id: 2)
#prediction.update_attributes(local_score: 8, visitant_score: 8)
Suggesting the problem is in the controller action:
app/controllers/predicitons_controller.rb
def update
respond_to do |format|
if #prediction.update(prediction_params)
format.json { render :show, status: :ok, location: #prediction }
format.js
else
#errorMessages = []
#prediction.errors.full_messages.each do |message|
#errorMessages << message
end
format.js {render template: 'predictions/update_error'}
end
end
end
I see no problem there. Additionally, it seems that the only request sent to the controller that updates successfully is:
"prediction"=><ActionController::Parameters {"participant_id"=>"1", "match_id"=>"1", "local_score"=>"1", "visitant_score"=>"1"}
And if I do these others, for example, it doesn't:
"prediction"=><ActionController::Parameters {"participant_id"=>"2", "match_id"=>"1", "local_score"=>"0", "visitant_score"=>"0"}
"prediction"=><ActionController::Parameters {"participant_id"=>"2", "match_id"=>"2", "local_score"=>"9", "visitant_score"=>"9"}
"prediction"=><ActionController::Parameters {"participant_id"=>"4", "match_id"=>"1", "local_score"=>"1", "visitant_score"=>"1"
All of these failed requests throw the "match_id already taken" error and update just fine in the console.
The controller request is touching the correct action with the method: :put. I've tried to change the syntax of the validation switching match_id for pool_id (which of course shouldn't make a difference) with no success.
Any, help would be appreciated.
app/models/prediction.rb
class MatchPrediction < ApplicationRecord
belongs_to :participant
belongs_to :match
validates :match_id, uniqueness: { scope: [:pool_participant_id] }
def correct_score?
if match.official_outcome
local_score == match.local_score && visitant_score == match.visitant_score ? true : false
end
end
def correct_outcome?
if match.official_outcome
predicted_outcome == match.official_outcome ? true : false
end
end
end
and server output pre rollback:
Started PUT "/pools/1/predictions/19" for 127.0.0.1 at 2018-03-29 18:17:11 +1100
Processing by PredictionsController#update as JS
Parameters: {"utf8"=>"✓", "authenticity_token"=>"SagJX+a7m0eCAdH7AdA0eYz6BVL1cesXYXOUkoe2FRynta6wyiWdskrC1007V1vyrIApPtdEQnVHWlzhSeJs5Q==", "prediction"=>{"participant_id"=>"4", "match_id"=>"2", "local_score"=>"7", "visitant_score"=>"0"}, "pool_id"=>"1", "id"=>"19"}
User Load (1.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 4], ["LIMIT", 1]]
↳ /Users/andres/.rvm/gems/ruby-2.5.0#global/gems/rack-2.0.4/lib/rack/tempfile_reaper.rb:15
Prediction Load (0.4ms) SELECT "predictions".* FROM "predictions" WHERE "predictions"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ app/controllers/predictions_controller.rb:86
(0.2ms) BEGIN
↳ app/controllers/predictions_controller.rb:55
Participant Load (0.2ms) SELECT "participants".* FROM "participants" WHERE "participants"."id" = $1 LIMIT $2 [["id", 4], ["LIMIT", 1]]
↳ app/controllers/predictions_controller.rb:55
Match Load (0.3ms) SELECT "matches".* FROM "matches" WHERE "matches"."id" = $1 LIMIT $2 [["id", 2], ["LIMIT", 1]]
↳ app/controllers/predictions_controller.rb:55
Prediction Exists (1.8ms) SELECT 1 AS one FROM "predictions" WHERE "predictions"."match_id" = $1 AND "predictions"."id" != $2 AND "predictions"."participant_id" = $3 LIMIT $4 [["match_id", 2], ["id", 1], ["participant_id", 4], ["LIMIT", 1]]
↳ app/controllers/predictions_controller.rb:55
Pool Load (0.5ms) SELECT "pools".* FROM "pools" WHERE "pools"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ app/models/prediction.rb:35
Round Load (0.6ms) SELECT "rounds".* FROM "rounds" INNER JOIN "groups" ON "rounds"."id" = "groups"."round_id" WHERE "groups"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ app/models/prediction.rb:21
(0.2ms) ROLLBACK
↳ app/controllers/predictions_controller.rb:55
Embarrassing mistake really. I missed checking how the Predictions Controller was setting #prediction. I had entred the params to find the object incorrectly as #GorillaApe suggested:
def set_match_prediction
#prediction = Prediction.find(params[:pool_id])
end
instead of:
def set_match_prediction
#prediction = Prediction.find(params[:id])
end
this in turn was always setting the same object thus raising the validation error in all but one of the instances which, by chance, its :id coincided with :pool_id.

How to delete another record in same table using after_destroy callback

I have a table in below columns
id, user_id, friend_user_id
If A is a friend of B then, I want to insert two records such as
1. user_id: 1, friend_user_id: 2
2. user_id: 2, friend_user_id: 1
I've did this using the after_create callback as below
after_create do
Friend.create(user_id: friend_user_id,friend_user_id: user_id)
end
I want to delete both records, if any one of the record has been deleted.
I've tried the after_destroy callback as below.
after_destroy do
Friend.where(friend_user_id: user_id,user_id: friend_user_id).first.destroy
end
But I'm getting the below error.
2.3.0 :002 > Friend.first.destroy
Friend Load (0.4ms) SELECT "friends".* FROM "friends" ORDER BY "friends"."id" ASC LIMIT 1
(0.2ms) begin transaction
SQL (0.7ms) DELETE FROM "friends" WHERE "friends"."id" = ? [["id", 10]]
Friend Load (0.3ms) SELECT "friends".* FROM "friends" WHERE "friends"."friend_user_id" = ? AND "friends"."user_id" = ? ORDER BY "friends"."id" ASC LIMIT 1 [["friend_user_id", 1], ["user_id", 2]]
SQL (0.1ms) DELETE FROM "friends" WHERE "friends"."id" = ? [["id", 11]]
Friend Load (0.1ms) SELECT "friends".* FROM "friends" WHERE "friends"."friend_user_id" = ? AND "friends"."user_id" = ? ORDER BY "friends"."id" ASC LIMIT 1 [["friend_user_id", 2], ["user_id", 1]]
(0.3ms) rollback transaction
NoMethodError: undefined method `destroy' for nil:NilClass
from /home/ubuntu/workspace/app/models/friend.rb:39:in `block in <class:Friend>'
I'm new to RoR. Any help would be appreciated.
Your code is quite wrong here, you will end up getting SystemStackError and that happens in both the places in after_create and after_destroy. What is happening is that when you destroy an element, then your after_destroy callback is executed which will again destroy an element and since it has deleted an element it will again execute a callback, but since you have deleted that element that is why you are getting this error NoMethodError: undefined methoddestroy' for nil:NilClass` because that element has already been deleted, to get a more clear picture use the same code and do a Friend.create and you will see what I am saying.

How is Ajax Destroy method for a nested resource is causing its parent resource to be destroyed?

I have resources set up like this:
resources :scoreboards do
resources :teams
end
On my scoreboard#show view page, I have a collection for the team model which generates a div for each team. Beside each team-div is a Delete button which routes to a method in the teams_controller to delete that team.
Here is a list of all the code associated with it:
The Delete Button next to a team
<div>team example</div> <%= link_to "Del", scoreboard_team_path(#scoreboard, team), remote: true, method: :delete, class: "btn btn-primary" %>
The Teams_controller method for the button
def destroy
#scoreboard = Scoreboard.find(params[:scoreboard_id])
#team = #scoreboard.teams.find(params[:id])
#team.destroy
respond_to do |format|
format.html {redirect_to scoreboard_url(#scoreboard)}
format.js
end
end
The destroy.js.erb file
$( "#team_<%=#team.id%>" ).hide();
Now the issue on hand is that whenever I click on a delete button really fast in quick succession(twice or more on one button), all the Ajax delete buttons stop working. This is most likely because the Scoreboard resource the teams were associated to has been deleted because I get the following error in the Scoreboards_Controller:
NoMethodError in ScoreboardsController#show
undefined method `teams' for nil:NilClass
def show
#scoreboard = Scoreboard.find_by_id(params[:id])
#team = #scoreboard.teams.build # new team form on the page
#comment = #scoreboard.comments.new
#schedule = #scoreboard.schedules.build
end
Then when I check my list of Scoreboards, the #Scoreboard resource which the #team was associated to does not exist anymore. Why is that happening?
Edit: Checking the development logs has clarified what is happening.
So I click delete really fast and it destroys the Team associated with that delete button. Since I manage to click the delete button twice before the destroy.js.erb files processes(hides the deleted div), the Teams_Controller#destroy method is run again on the deleted team, however there is nothing to delete, therefore the Teams_Controller#destroy proceeds to redirect to #scoreboard. Now for some reason, the Scoreboards_Controller#destroy executes and deletes the #scoreboard and after that it attempts to redirect again and encounters a routing error because the scoreboard no longer exists.
Here is some of the log for clarification:
Started DELETE "/scoreboards/45/teams/478" for 99.000.000.000 at 2015-12-12 03:54:09 +0000
Processing by TeamsController#destroy as JS
Parameters: {"scoreboard_id"=>"45", "id"=>"478"}
[1m[36mScoreboard Load (0.3ms)[0m [1mSELECT "scoreboards".* FROM "scoreboards" WHERE "scoreboards"."id" = ? ORDER BY "scoreboards"."created_at" DESC LIMIT 1[0m [["id", 45]]
[1m[35mTeam Load (0.2ms)[0m SELECT "teams".* FROM "teams" WHERE "teams"."scoreboard_id" = ? AND "teams"."id" = ? LIMIT 1 [["scoreboard_id", 45], ["id", 478]]
[1m[36m (0.3ms)[0m [1mbegin transaction[0m
[1m[35mSQL (0.4ms)[0m DELETE FROM "teams" WHERE "teams"."id" = ? [["id", 478]]
[1m[36m (10.6ms)[0m [1mcommit transaction[0m
Rendered teams/destroy.js.erb (0.2ms)
Completed 200 OK in 48ms (Views: 28.1ms | ActiveRecord: 11.8ms)
Started DELETE "/scoreboards/45/teams/478" for 99.000.000.000 at 2015-12-12 03:54:09 +0000
Processing by TeamsController#destroy as JS
Parameters: {"scoreboard_id"=>"45", "id"=>"478"}
[1m[35mScoreboard Load (0.3ms)[0m SELECT "scoreboards".* FROM "scoreboards" WHERE "scoreboards"."id" = ? ORDER BY "scoreboards"."created_at" DESC LIMIT 1 [["id", 45]]
[1m[36mTeam Load (0.2ms)[0m [1mSELECT "teams".* FROM "teams" WHERE "teams"."scoreboard_id" = ? AND "teams"."id" = ? LIMIT 1[0m [["scoreboard_id", 45], ["id", 478]]
Redirected to https://score-app-kpauls.c9.io/scoreboards/45
Completed 302 Found in 19ms (ActiveRecord: 0.5ms)
Started DELETE "/scoreboards/45" for 99.000.000.000 at 2015-12-12 03:54:09 +0000
Processing by ScoreboardsController#destroy as JS
Parameters: {"id"=>"45"}
[1m[35mScoreboard Load (0.2ms)[0m SELECT "scoreboards".* FROM "scoreboards" WHERE "scoreboards"."id" = ? ORDER BY "scoreboards"."created_at" DESC LIMIT 1 [["id", 45]]
[1m[36mUser Load (0.1ms)[0m [1mSELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1[0m [["id", 105]]
[1m[35mCACHE (0.0ms)[0m SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 105]]
[1m[36mCACHE (0.0ms)[0m [1mSELECT "scoreboards".* FROM "scoreboards" WHERE "scoreboards"."id" = ? ORDER BY "scoreboards"."created_at" DESC LIMIT 1[0m [["id", "45"]]
[1m[35m (0.2ms)[0m begin transaction
[1m[36mTeam Load (0.1ms)[0m [1mSELECT "teams".* FROM "teams" WHERE "teams"."scoreboard_id" = ?[0m [["scoreboard_id", 45]]
[1m[35mSQL (0.3ms)[0m DELETE FROM "teams" WHERE "teams"."id" = ? [["id", 479]]
[1m[36mSQL (0.0ms)[0m [1mDELETE FROM "teams" WHERE "teams"."id" = ?[0m [["id", 480]]
[1m[35mSQL (0.1ms)[0m DELETE FROM "teams" WHERE "teams"."id" = ? [["id", 481]]
[1m[36mSQL (0.1ms)[0m [1mDELETE FROM "teams" WHERE "teams"."id" = ?[0m [["id", 482]]
[1m[35mComment Load (0.1ms)[0m SELECT "comments".* FROM "comments" WHERE "comments"."scoreboard_id" = ? [["scoreboard_id", 45]]
[1m[36mSQL (0.1ms)[0m [1mDELETE FROM "scoreboards" WHERE "scoreboards"."id" = ?[0m [["id", 45]]
[1m[35m (14.1ms)[0m commit transaction
Redirected to https://score-app-kpauls.c9.io/scoreboards
Completed 302 Found in 39ms (ActiveRecord: 15.6ms)
After this the program encounters a routing error. I will continue to look into it but if anyone could help find the reason why scoreboards_controller#destroy is being called, it'd be really appreciated.
Update on the Problem:
So I have figured out the problem. I had these two methods in my application_controller file.
rescue_from ActiveRecord::RecordNotFound do
flash[:warning] = 'Resource not found.'
redirect_back_or root_path
end
def redirect_back_or(path)
redirect_to request.referer || path
end
Whenever, I would click on the delete button twice in quick succession, the destroy action would be rerouted to the scoreboard#show page and proceed to call the destroy method for that resource on the second click. This is because the #team which the destroy method was being called for had already been destroyed in the first click therefore requesting a redirect. I did get the flash messages after I refreshed the page to go the home page but dismissed them as relevant at first but they were key to the conclusion.
The code infrastructure looks good, I would recommend looking at the associations and making sure you don't have dependent: :destroy on team belongs_to :scoreboard
--
In regards the problem of multiple "delete" button clicking, the problem looks like you're getting a redirect to the parent resource. I don't have any reason why this would be the case, except that perhaps Rails has an inbuilt set of functionality to load "parent" routes if the child fails.
The way I would tackle the fix is to use conditions:
def destroy
#scoreboard = Scoreboard.find(params[:scoreboard_id])
#team = #scoreboard.teams.find params[:id]
if #team.destroy
respond_to do |format|
format.html {redirect_to scoreboard_url(#scoreboard)} #-> could this be the reason for the redirect?????
format.js
end
else
redirect_to scoreboard_teams_path(#scoreboard), notice: "Team Already Deleted"
end
end
I'd also look at conditioning the #team -- if #team && #team.destroy - I can refactor if you wanted more information.
Doing this will give you a definite flow which can handle exceptions. I think the problem is that when you click the delete button (and the record no longer exists), Rails is unable to handle the exception.
The inbuilt way that Rails comes back with errors is to redirect to the object_path(#object), and show the errors (like you've got in your format.html).
Therefore, I would guess that Rails is trying to take you back to #scoreboard (scoreboard_path(#scoreboard)), and since you have method: :delete, it's running the destroy method for that controller.
To fix it, you'd need to use the conditions above to let Rails know what to do in case of problems.
In your teams_controller in the destroy action, I suggest you change this line:
#team = #scoreboard.teams.find(params[:id])
for
#team = Team.find(params[:id])

Having trouble with create method in CommentsController

This is my current Create method in CommentsController
def create
#place = Place.find(params[:place_id])
#comment = #place.comments.create(comment_params)
#comment.user = current_user
if #comment.save
redirect_to place_path(#place)
else
render "comments/_form"
end
end
I was told by someone that this hits the database twice. Upon checking the logs, this is the result:
Started GET "/places/9" for 127.0.0.1 at 2015-01-22 15:01:47 -0800
Processing by PlacesController#show as HTML
Parameters: {"id"=>"9"}
Place Load (0.3ms) SELECT "places".* FROM "places" WHERE "places"."id" = ? LIMIT 1 [["id", 9]]
Comment Load (0.2ms) SELECT "comments".* FROM "comments" WHERE "comments"."place_id" = ? [["place_id", 9]]
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 2]]
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 2]]
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 2]]
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 2]]
Rendered comments/_comment.html.erb (8.0ms)
Rendered comments/_form.html.erb (2.7ms)
Rendered places/show.html.erb within layouts/application (73.3ms)
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 2]]
Completed 200 OK in 532ms (Views: 529.4ms | ActiveRecord: 1.0ms)
So obviously, I don't want to be inefficient in my code.
This is what the person suggested instead:
comment_params[:user_id] = current_user.id
if #places.comments.create(comment_params)
.....
else
....
end
So ... I rewrote the Create method to this:
def create
#place = Place.find(params[:place_id])
#comment = #place.comments.create(comment_params)
comment_params[:user_id] = current_user.id
if #places.comments.create(comment_params)
redirect_to place_path(#place)
else
render "comments/_form"
end
end
Upon the rewritten Create method, I keep getting this error when I try to leave a comment: undefined method comments for nil:NilClass
Help me to understand how to rewrite this Create method correctly, please?
SideNote - Not sure if it's relevant if yes, please address it, if no, please ignore
Upon checking the last comment in the rails console, I was startled to see that the user_id is nil, whereas for place, it's not.
Place belongs to User
Comment belongs to User
User has many of both Place and Comment
You're calling create twice, which would result in the comment being posted twice, which is probably not what you have intended.
The first time you call create, it has no user_id assigned, that's the reason the user_id is set to null in the database (hint: use a presence validation on the user_id in your comment model).
The second time you call create you call it on #places instead of #place (typo). This causes your nil error.
Here is another way to solve your problem:
def create
#place = Place.find(params[:place_id])
#comment = #place.comments.build(comment_params)
#comment.user_id = current_user.id
if #comment.save(comment_params)
redirect_to place_path(#place)
else
render "comments/_form"
end
end
The difference is, that we first "build" a comment without creating it, but it will be filled with the ID of the place etc.
In a next step we add the current user's id and then we call save on the object, which hits the database.

Resources