Search code examples
ruby-on-railsrubycontrollerdevisenested

Rails nested resource returning incorrect ID in params hash


I am trying to make a simple app for submitting goals of users, who can then mark the goals as completed. I can complete the "new" action fine, but when I'm trying to "edit," the params include the ID of the user instead of the ID of the goal.

For my form partial, I'm using a simple form_with:

> <%= form_with(model: [@user, @goal]) do |f| %>
>     <%= f.label :goal_title, "Title" %>
>     <%= f.text_field :goal_title %>
>     <%= f.label :description, "Description" %>
>     <%= f.text_area :description %>
>     <%= f.select(:public, [['Public', true], ['Private', false]]) %>
>     <%= f.select(:completed, [['Completed', true], ['Not Completed', false]]) %>
>     <%= f.submit "Submit" %> 
>     <% end %>

My controller is as follows:

class GoalsController < ApplicationController
    def index
        @goals = current_user.goals
        render :index
    end

    def new
        @goal = Goal.new(user_id: current_user.id)
        @user = current_user
        render :new
    end

    def create
        @goal = current_user.goals.new(goal_params)
        if @goal.save
            redirect_to user_goals_path
            flash[:notice] = "Goal saved"
        else
            flash.now[:errors] = @goal.errors.full_messages
            render :new
        end
    end

    def update
        @goal = Goal.find_by(id: params[:id])
        if @goal.update_attributes(goal_params)
            redirect_to user_goals_path
            flash[:notice] = "Goal updated"
        else
            flash.now[:errors] = @goal.errors.full_messages
            render :edit
        end
    end

    def edit
        @goal = Goal.find_by(id: params[:id])
        @user = current_user
        render :edit
    end

    def homepage
        @goals = Goal.all
        render :index
    end

    private

    def goal_params
       params.require(:goal).permit(:goal_title, :description, :completed, :public)
    end 
end

And my current routes are:

                       Prefix Verb   URI Pattern                                                                              Controller#Action
             new_user_session GET    /users/sign_in(.:format)                                                                 devise/sessions#new
                 user_session POST   /users/sign_in(.:format)                                                                 devise/sessions#create
         destroy_user_session DELETE /users/sign_out(.:format)                                                                devise/sessions#destroy
            new_user_password GET    /users/password/new(.:format)                                                            devise/passwords#new
           edit_user_password GET    /users/password/edit(.:format)                                                           devise/passwords#edit
                user_password PATCH  /users/password(.:format)                                                                devise/passwords#update
                              PUT    /users/password(.:format)                                                                devise/passwords#update
                              POST   /users/password(.:format)                                                                devise/passwords#create
     cancel_user_registration GET    /users/cancel(.:format)                                                                  devise/registrations#cancel
        new_user_registration GET    /users/sign_up(.:format)                                                                 devise/registrations#new
       edit_user_registration GET    /users/edit(.:format)                                                                    devise/registrations#edit
            user_registration PATCH  /users(.:format)                                                                         devise/registrations#update
                              PUT    /users(.:format)                                                                         devise/registrations#update
                              DELETE /users(.:format)                                                                         devise/registrations#destroy
                              POST   /users(.:format)                                                                         devise/registrations#create
                   user_goals GET    /user/goals(.:format)                                                                    goals#index
                              POST   /user/goals(.:format)                                                                    goals#create
                new_user_goal GET    /user/goals/new(.:format)                                                                goals#new
               edit_user_goal GET    /user/goals/:id/edit(.:format)                                                           goals#edit
                    user_goal PATCH  /user/goals/:id(.:format)                                                                goals#update
                              PUT    /user/goals/:id(.:format)                                                                goals#update
                              DELETE /user/goals/:id(.:format)                                                                goals#destroy
                     new_user GET    /user/new(.:format)                                                                      users#new
                    edit_user GET    /user/edit(.:format)                                                                     users#edit
                         user GET    /user(.:format)                                                                          users#show
                              PATCH  /user(.:format)                                                                          users#update
                              PUT    /user(.:format)                                                                          users#update
                              DELETE /user(.:format)                                                                          users#destroy
                              POST   /user(.:format)                                                                          users#create
                        goals GET    /goals(.:format)                                                                         goals#homepage
                         root GET    /                                                                                        goals#homepage

The params hash in the "edit" show fine: <ActionController::Parameters {"controller"=>"goals", "action"=>"edit", "id"=>"197"} permitted: false> but the params for "update" show <ActionController::Parameters {"_method"=>"patch", "goal"=>{"goal_title"=>"Updated goal", "description"=>"Updated description", "public"=>"true", "completed"=>"true"}, "commit"=>"Submit", "controller"=>"goals", "action"=>"update", "id"=>"379"} permitted: false>. The ID should be 197 (goal ID) not 379 (user ID).

Any suggestions? Thanks.


Solution

  • If you look at your routes again:

                       user_goals GET    /user/goals(.:format)                                                                    goals#index
                                  POST   /user/goals(.:format)                                                                    goals#create
                    new_user_goal GET    /user/goals/new(.:format)                                                                goals#new
                   edit_user_goal GET    /user/goals/:id/edit(.:format)                                                           goals#edit
                        user_goal PATCH  /user/goals/:id(.:format)                                                                goals#update
                                  PUT    /user/goals/:id(.:format)                                                                goals#update
                                  DELETE /user/goals/:id(.:format) 
    

    Goal is not nested inside user resource, instead user here acts as namespace, therefore when you call form_with(model: [@user, @goal]) Rails will pick user.id as id params.

    Solution:

    1. If you want it to be a nested resource, you need to change your routes config in config/routes.rb to something like this.

      resources :users do
        resources :goals
      end
      
    2. If you don't need nested resource, just change form_with(model: [@user, @goal]) to form_with(model: @goal)