1

I'm programming a Book library system with a SQL Lite database in the background. Via the console I can add and update books without a problem. And I have already made a view where I can add new books and it works great.

But now I'm trying to make a view where I can update/edit books but it doesn't work.

I'm getting an error message because its trying to call the edit method from my book_controller.rb without a book :id. Does someone have an idea why it's not calling the method with a :id?

error

Couldn't find Book without an ID 
app/controllers/book_controller.rb:30:in `edit'

routs.rb

Rails.application.routes.draw do   
  get 'users/show'   
  get 'forms/show'
  get 'tabelle/show'   
  get 'photography/show'   
  get 'computer_scientist/show'   
  get 'switzerland/show'   
  get 'book/index', as: 'book_index'   
  get 'book/show'   
  get 'book/new'   
  get 'book/create'   
  get 'book/book_prams'   
  get 'book/edit'
  get 'book/update'   
  get 'book/destroy'   
  get 'subject/show'   
  get '/book/show/:id', to: 'book#show', as: 'show_book'   
  get '/book/new', to: 'book#new', as: 'new_book'   
  get '/book/edit', to: 'book#edit', as: 'edit_book'
  delete '/book/destroy/:id', to: 'book#destroy', as: 'delete_book'   
  root 'pages#home'   
  post 'book/create', as: 'create_book'   
  put 'book/update/:id', to: 'book#update', as: 'update_book' 
end

book_controller.rb

class BookController < ApplicationController   
  def index
    @books = Book.all   
  end

  def show
    @book = Book.find(params[:id])
  end

  def new
    @book = Book.new
    @subjects = Subject.all   
  end

  def create
    @book = Book.new(book_params)
    if @book.save
      redirect_to action: "index"
    else
      @subjects = Subject.all
      render action: "new"
    end   
  end

  def book_params
    params.require(:book).permit(:title, :price, :subject_id, :description)   
  end

  def edit
    @book = Book.find(params[:id])
    @subjects = Subject.all   
  end

  def update
    @book = Book.find(params[:id])
    if @book.update_attributes(book_params)
      redirect_to action: "index"
    else
      @subjects = Subject.all
      render action: "edit"
    end   
  end

  def destroy
    Book.find(params[:id]).destroy
    redirect_to action: "index"   
  end 
end

edit.html.erb

  <main>   
    <section class="form-group">
      <%= form_with model: @book, url: update_book_path(@book.id) do |f| %>
        <p class="form-control">Title <%= f.text_field :title %></p>
        <p class="form-control">Price <%= f.text_field :price %></p>
        <p class="form-control">Subject <%= f.collection_select :subject_id, @subjects, :id, :name %></p>
        <p class="form-control">Description <%= f.text_area :description %></p>
        <p class="form-control"><%= f.submit "Buch speichern"%></p>
        <p class="form-control"><%= link_to "Abbrechen", book_index_path %></p>
      <% end %>   
    </section> 
  </main>

index.html.erb

<div class="container">   
  <% if @books.blank? %>
    <p>Es sind keine Bücher im System.</p>
  <%else %>
    <table class="table table-hover">
      <thead class="thead-light">
        <tr>
          <th>Buch</th>
          <th colspan="2">Aktion</th>
        </tr>
      </thead>
      <tbody>
        <%@books.each do |b| %>
          <tr>
            <td><%= link_to b.title, show_book_path(b.id) %></td>
            <td><%= link_to "Ändern", edit_book_path(b.id) %></td>
            <td><%= link_to "Löschen", delete_book_path(b.id), method: :delete,
             data: { confirm: "Sind Sie sicher?" } %></td>
          </tr>
        <% end %>
      </tbody>
    </table>
  <%end %>   
  <%= link_to "Neues Buch erfassen", new_book_path %> 
</div>

strong text

<main>   
  <section class="form-group">
    <%= form_with model: Book.new, url: create_book_path do |f| %>
      <p class="form-control">Title <%= f.text_field :title %></p>
      <p class="form-control">Price <%= f.text_field :price %></p>
      <p class="form-control">Subject <%= f.collection_select :subject_id, @subjects, :id, :name %></p>
      <p class="form-control">Description <%= f.text_area :description %></p>
      <p class="form-control"><%= submit_tag "Buch speichern" %></p>
      <p class="form-control"><%= link_to "Abbrechen", book_index_path %></p>
    <% end %>   
  </section> 
</main>
1
  • Can you edit your question so it makes sense? Code shouldn't be quoted, it should be syntax highlighted. HTML snippets must be syntax highlighted if you want the HTML to be visible. Commented Sep 16, 2020 at 13:35

2 Answers 2

1

The problem is your routes. (And as mentioned in the other answer, the Rails conventions are not followed, be careful with singular/plural).

See how in the controller you do this in the edit method:

@book = Book.find(params[:id])

The params hash has different sources, one of them is the url. In Rails you're expected to pass the ID of the record you want to edit/update through the url. So your route should look like this:

get "books/:id/edit", to: "books#edit"

Also watch out for the update route, the one tyou have is not the standard Rails way. It should be:

patch 'books/:id/update', to: 'book#update', as: 'update_book'

Look into the resources method for routes here: https://guides.rubyonrails.org/routing.html#resource-routing-the-rails-default

Sign up to request clarification or add additional context in comments.

Comments

1

There's a number of things wrong with your code. Rails encourages "convention over configuration" and you are breaking from convention in a number of ways.

First, you should use the conventional BooksController, not BookController.

Second, you should be using conventional resource-oriented routes, not hand-crafting all the book routes. Something like:

Rails.application.routes.draw do

  ...

  resources :books

  ...
 
end

Which will give you:

    books GET    /books(.:format)            books#index
          POST   /books(.:format)            books#create
 new_book GET    /books/new(.:format)        books#new
edit_book GET    /books/:id/edit(.:format)   books#edit
     book GET    /books/:id(.:format)        books#show
          PATCH  /books/:id(.:format)        books#update
          PUT    /books/:id(.:format)        books#update
          DELETE /books/:id(.:format)        books#destroy

Then, in your index.html.erb, you can do:

<% @books.each do |book| %>
  <tr>
    <td><%= link_to book.title, book %></td>
    <td><%= link_to "Ändern", edit_book_path(book) %></td>
    <td><%= link_to "Löschen", book, method: :delete, data: { confirm: "Sind Sie sicher?" } %></td>
  </tr>
<% end %>

1 Comment

Thank you for your feedback. I will take a closer look at the convetions. ;)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.