peterbie
peterbie

Reputation: 5

undefined method or my Active Record mistakes

Sorry if my problem will be silly but Rails are new to me. I made two models and two controllers. My problems were started after I made second model and added reference to the first one.

class SentencesController < ApplicationController
    before_action :find_story

    def create      
        @sentence = find_story.sentences.build(sentence_params)
        if @sentence.save
           flash[:success] = "You wrote the continuation!"
           render 'stories/show'
        else
          render 'stories/show'
        end
    end

  private

    def sentence_params
      params.require(:sentence).permit(:content)
    end

    def find_story
        @story = Story.find(params[:id])
    end
end

and this:

class StoriesController < ApplicationController 

........

    def show
        @story = Story.find(params[:id])
        @sentence = @story.sentences.build
    end 

.........


end

And I have a problem with defining instance variable @story = Story.find(params[:id]). Error: ActiveRecord::RecordNotFound in SentencesController#create. I have tried many combinations.

This is my migrate files:

class CreateStories < ActiveRecord::Migration[5.1]
  def change
    create_table :stories do |t|
      t.string :title
      t.text :content

      t.timestamps
    end
  end
end

class CreateSentences < ActiveRecord::Migration[5.1]
  def change
    create_table :sentences do |t|
      t.text :content
      t.references :story, foreign_key: true

      t.timestamps
    end
    add_index :sentences, [:story_id, :created_at]
  end
end

What did I do wrong?

EDIT (routes):

Rails.application.routes.draw do
    root 'stories#index'
    get 'stories/show'
  get 'stories/new'
  resources :stories
  resources :sentences, only: [:create]
end

and schema:

ActiveRecord::Schema.define(version: 20180322121215) do

  create_table "sentences", force: :cascade do |t|
    t.text "content"
    t.integer "story_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["story_id"], name: "index_sentences_on_story_id"
  end

  create_table "stories", force: :cascade do |t|
    t.string "title"
    t.text "content"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

end

Upvotes: 0

Views: 678

Answers (2)

Matt
Matt

Reputation: 337

There are a couple of reasonable ways you can find the story in your sentences controller.

  1. You can add a story_id field to your form and submit it as a param along with the sentence content. Just make sure to add it to sentence_params in the controller so it's not ignored.

    def sentence_params
      params.require(:sentence).permit(:content, :story_id)
    end
    

    And then you'll need to update your find_story method in the controller to:

    @story = Story.find(sentence_params[:story_id])
    
  2. You can set up nested resources in your routes file (where the sentences resource is nested within the stories resource). That will give you access to the story_id from the route itself (ie. you wouldn't need to submit the story_id through the form).

    And if you go this way, you'll also need to tweak the find_story method in the controller, but this time it should be:

    @story = Story.find(params[:story_id])
    

Upvotes: 0

jvillian
jvillian

Reputation: 20263

As stated in the comments, you probably want your routes to look something like:

resources :stories do 
  resources :sentences, only: [:create]
end

Which will give you:

 story_sentences POST   /stories/:story_id/sentences(.:format)   sentences#create
         stories GET    /stories(.:format)                       stories#index
                 POST   /stories(.:format)                       stories#create
       new_story GET    /stories/new(.:format)                   stories#new
      edit_story GET    /stories/:id/edit(.:format)              stories#edit
           story GET    /stories/:id(.:format)                   stories#show
                 PATCH  /stories/:id(.:format)                   stories#update
                 PUT    /stories/:id(.:format)                   stories#update
                 DELETE /stories/:id(.:format)                   stories#destroy

Which you might use something like:

<%= form_tag story_sentences_path(@story) do %>
  ...
<% end %>

Then, as Matt said, change your find to:

@story = Story.find(params[:story_id])

Upvotes: 3

Related Questions