1

I've looked at all kinds of threads on the same error and tried different things people suggested, but can't seem to figure this one out. I'm using Rails 5 and keep getting this error when I try to submit my form:

Step reference must exist

Here are my routes

Rails.application.routes.draw do
  resources :jobs do
    resources :steps, except: [:index], controller: 'jobs/steps'
      member do
        patch :complete
      end
  end

  get '/job/:id/next_step', to: 'jobs#next_step', as: 'next_step_jobs'

  root "pages#home"
end

I have a Job model and Step model

class Job < ActiveRecord::Base
  has_many :steps, dependent: :destroy
  accepts_nested_attributes_for :steps

  def step_completed?
    !steps.last.completed_at.blank?
  end
end

class Step < ActiveRecord::Base
  belongs_to :job, optional: true
  belongs_to :step_reference
end

Here are both controllers

class JobsController < ApplicationController
  before_action :set_job, only: [:show, :edit, :next_step, :update]

  # GET /jobs
  def index
    @dashboard = Dashboard.new
  end

  # GET /jobs/:id
  def show
  end

  # GET /jobs/new
  def new
    @job = Job.new
  end

  # GET /jobs/:id/edit
  def edit
  end

  # POST /jobs
  def create
    @job = Job.create(job_params)
    if @job.save
      # redirect to the next_step_path to ask questions
      redirect_to next_step_jobs_path(@job), flash: { success: "Job successfully added" }
    else
      redirect_to new_job_path, flash: { danger: @job.errors.full_messages.join(", ") }
    end
  end

  # PATCH /jobs/:id
  def update
    if @job.update(job_params)
      redirect_to jobs_path, flash: { success: "Job successfully updated" }
    else
      redirect_to edit_jobs_path(@job), flash: { danger: @job.errors.full_messages.join(", ") }
    end
  end

  # GET /jobs/:id/next_step
  def next_step
    # get question number from query string
    @q = params[:q]
    # create new instance of step
    @next = Next.new
    # pull the next_step object with question and answers to pass to the view
    @next_step = @next.next_step(@job, @q)

    if @next_step[:answers][0][:text].nil?
      redirect_to new_job_step_path(@job)
    else
      @next_step
    end
  end

  private
    def set_job
      @job = Job.find(params[:id])
    end

    def job_params
      params.require(:job).permit(:company, :position, :contact, :contact_email,
      :posting_url, steps_attributes: [:step_reference_id, :job_id, :completed_at, :next_step_date])
    end
end



class Jobs::StepsController < ApplicationController
  before_action :set_job
  before_action :set_step, only: [:show, :edit, :update, :complete]

  # GET /jobs/:job_id/steps/:id
  def show
  end

  # GET /jobs/:job_id/steps/new
  def new
    @step = @job.steps.new
  end

  # GET /jobs/:job_id/steps/:id/edit
  def edit
  end

  # POST /jobs/:job_id/steps/
  def create
    @step = @job.steps.create(step_params)
    if @step.save
      redirect_to jobs_path, flash: { success: "Next step successfully added." }
    else
      redirect_to jobs_path, flash: { danger: @step.errors.full_messages.join(", ") }
    end
  end

  # PATCH /jobs/:job_id/steps/:id
  def update
    if @step.update(step_params)
      redirect_to jobs_path, flash: { success: "Next step successfully updated." }
    else
      redirect_to edit_job_step_path, flash: { danger: @step.errors.full_messages.join(", ") }
    end
  end

  # PATCH /jobs/:job_id/steps/:id/complete
  def complete
    if @step.update_attribute(:completed_at, Date.today)
      redirect_to jobs_path, flash: { success: "Job step completed" }
    else
      redirect_to jobs_path, flash: { danger: @step.errors.full_messages.join(", ") }
    end
  end

  private
    def set_job
      @job = Job.find(params[:job_id])
    end

    def set_step
      @step = Step.find(params[:id])
    end

    def step_params
      params.require(:step).permit(:step_reference_id, :job_id, :completed_at, :next_step_date)
    end
end

And here's my view for /jobs/:job_id/steps/new

<h1 class="text-xs-center">New Step</h1>

<%= form_for @step, url: job_steps_path(@job) do |f| %>
  <div class="form-group">
    <%= f.label :next_step_date %>
    <%= f.date_field :next_step_date, class: "form-control", id: "add-next-date-field" %>
  </div>
  <%= f.submit 'Save', class: "btn btn-primary" %>
<% end %>

So the form should be submitting to the StepsController's create method, but when it submits I get the "Step reference must exist" error. Based on other threads it seems like this might have to do with the strong parameters, but everything looks pretty standard to me.

Here's the log from terminal

Started POST "/jobs/1/steps" for ::1 at 2017-01-27 14:28:46 -0500
Processing by Jobs::StepsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"0hYvZArkUXj7WZw9L04ovYUSm4txz14pk8POgvJxNsdvvQ7bwQLusz5WW2u0wDGgT81k6mwDkMZLyaFIVxZW5w==", "step"=>{"next_step_date"=>"2017-01-31"}, "commit"=>"Save", "job_id"=>"1"}
  Job Load (0.2ms)  SELECT  "jobs".* FROM "jobs" WHERE "jobs"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
   (0.1ms)  begin transaction
  StepReference Load (0.1ms)  SELECT  "step_references".* FROM "step_references" WHERE "step_references"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
   (0.1ms)  commit transaction
   (0.1ms)  begin transaction
   (0.0ms)  rollback transaction
Redirected to http://localhost:3000/jobs
Completed 302 Found in 28ms (ActiveRecord: 1.0ms)

I've been fooling around with this for hours, so if anyone can shed some light I'd be extremely greatful!!!

Danny
  • 25
  • 1
  • 7

1 Answers1

0

I think your problem isn't with your params, but rather the way you've built your form. Typically, the way to do forms with nested attributes is to use the fields_for helper (info here).

So your form should look more like this:

<%= form_for @job do |f| %>
  <%= f.fields_for :steps do |steps| %>
    <div class="form-group">
      <%= steps.label :next_step_date %>
      <%= steps.date_field :next_step_date, class: "form-control", id: "add-next-date-field" %>
    </div>
  <% end %>
  <%= f.submit 'Save', class: "btn btn-primary" %>
<% end %>
oolong
  • 665
  • 6
  • 20
  • I should have mentioned this before, but I tried that approach as well. When I do that, it automatically routes to 'Jobs#Update' and I get the error "param is missing or the value is empty: job". – Danny Jan 27 '17 at 16:48
  • Are you trying to post to 'Steps#create'? If that's the case, I don't think you even need a nested attributes form. You would just create the step and associate it to the job in the controller. You would use the nested attributes form when you want to create/update a job while also creating/updating its steps – oolong Jan 27 '17 at 16:53
  • Yeah, I would like to just post to 'Steps#create'. I thought that's what my original form would do. How should the form look in that case? – Danny Jan 27 '17 at 17:00
  • I updated my answer to give an example for what I think you're trying to do. If you're posting to Steps#create from Steps#new, you should only need a basic `form_for`, and the logic you have in your `StepsController` should work fine. – oolong Jan 27 '17 at 17:08
  • Tried that as well, but then I get "First argument in form cannot contain nil or be empty". The params are {"q"=>"1.41", "id"=>"6"}. Could it have something to do with the param being "id" rather than "job_id"? – Danny Jan 27 '17 at 17:13
  • Weird, your `@step` is coming in as nil in your view? Which page's params are those? Steps#new or Steps#create? – oolong Jan 27 '17 at 17:15
  • So it's actually coming from a different page Jobs#next_step, where I realized I hadn't set `@step`. If I do `@step = @job.steps.new` I no longer get the nil error, but I'm back to the Step reference must exist. Maybe next_step is in the wrong controller? I wasn't really sure how to set it up. – Danny Jan 27 '17 at 17:28
  • Are you able to verify that when you submit the form, it goes to Steps#create? What params are you getting? – oolong Jan 27 '17 at 18:45
  • Yes, it's submitting to jobs/steps#create and here are the params `Parameters: {"utf8"=>"✓", "authenticity_token"=>"40Ja3e9dVdyFtuMo6nTfZNIZW+XoAlmluRxCTwVeecJe6XtiJLvqF0C5JH5x+sZ5GMakhPXOl0phFi2FoDkZ4g==", "step"=>{"next_step_date"=>"2017-01-28"}, "commit"=>"Save", "job_id"=>"3"}` – Danny Jan 27 '17 at 19:06
  • What line is triggering the error? Is it the `@step = @job.steps.create(step_params)`? Is there a backtrace you could post in your question? – oolong Jan 27 '17 at 19:14
  • Yeah, that's the line triggering the error. I've updated the original post with everything I have. Not sure what else to provide. Thanks! – Danny Jan 27 '17 at 19:38
  • Hey, what's a StepReference? It looks like you are having problems because `Step` belongs to StepReference without the `optional: true` flag, so the step you are trying to create isn't valid. Did you want StepReference to be required? – oolong Jan 27 '17 at 19:52
  • YES!!! That was it. I want StepReference to be required, but I wasn't passing in a value for it to my form. – Danny Jan 27 '17 at 20:34