I am creating a Rails library application in which the has_many genre of a book and belongs_to book of the same genre. I need to use form objects because my data model will end up with a lot of different relationships, and because I want to learn how to use form objects. I founded a form object outside of Rails Cast # 416 Form Objects. My form, form object, models, and controller all work. New books are being created, and they are related to genres, but they are all a genre of the genre. I am using find_or_create_by . I think the problem is book_form.rb , where @genre ||= Genre.find_by(name: :name) is not actually passed in the genre information from the form. My relevant code is as follows:
Model book.rb
class Book < ActiveRecord::Base belongs_to :genre before_save :convert_isbn . . . end
Model genre.rb
class Genre < ActiveRecord::Base has_many :books, dependent: :destroy validates_associated :books end
book_form.rb
class BookForm include ActiveModel::Model def self.model_name ActiveModel::Name.new(self, nil, "Book") end . . . validates :name, presence: true delegate :name, to: :genre delegate :title, :year, :pages, :isbn, :summary, :volume_a, :volume_b, to: :book def genre @genre ||= Genre.find_by(name: :name) end def book @book ||= genre.books.build end def submit(params) genre.attributes = params.slice(:name).permit(:name) book.attributes = params.slice(:title, :year, :pages, :isbn, :summary, :volume_a,:volume_b).permit(:title, :year, :pages, :isbn, :summary, :volume_a,:volume_b) if valid? genre.save! book.save! true else false end end end
books_controller.rb
class BooksController < ApplicationController before_action :admin_user, only: [:new, :edit, :destroy] def new @book_form = BookForm.new @genres = Genre.all end def create @book_form = BookForm.new @genres = Genre.all if @book_form.submit(params[:book]) flash[:success] = "You've added a new book to the library!" redirect_to @book_form else render 'new' end end
View new.html.erb
<%= form_for @book_form do |f| %> <%=render 'shared/error_messages', object: f.object %> <%= f.label :title %> <%= f.text_field :title, class: 'form-control' %> . . . <%= f.label :genre %><br> <%= collection_select(:genre, :name, Genre.all, :name, :name) %> <br> <br> <%= f.submit "Add book to library", class: "btn btn-primary" %> <% end %>
Using Pry pearls, I get this from the server when creating the book:
Started POST "/books" for ::1 at 2016-02-22 14:19:20 -0700 Processing by BooksController#create as HTML Parameters: {"utf8"=>"β", "authenticity_token"=>"bPusgyl9n+p07eQsEAe9CpSsithtkg33HMifj8KTsidv3GDLuhjibOC7d2mm5boC4w7ZUne64R4n4OMQotDE4g==", "book"=>{"title"=>"test", "year"=>"2016", "pages"=>"222", "isbn"=>"9780-xx-xx-xx", "summary"=>"fake summary", "volume_a"=>"1", "volume_b"=>"2"}, "genre"=>{"name"=>"Mystery"}, "commit"=>"Add book to library"} From: /home/nathaniel/rails_apps/allredlib/app/forms/book_form.rb @ line 38 BookForm#submit: 37: def submit(params) => 38: binding.pry 39: genre.attributes = params.slice(:name).permit(:name) 40: book.attributes = params.slice(:title, :year, :pages, :isbn, :summary, :volume_a,:volume_b).permit(:title, :year, :pages, :isbn, :summary, :volume_a,:volume_b) 41: if valid? 42: genre.save! 43: book.save! 44: true 45: else 46: false 47: end 48: end
So the genre of the book is passed by parameters, but I am addressing it incorrectly in my book form. If I comment on binding.pry , the form creates a new book with the genre "name" instead of the genre "Mystery", as if I want it to be done.
When I @genre on the rails when using binding.pry, I get
[1] pry(#<BookForm>)> @genre => #<Genre:0x007f19554e1220 id: 20, name: "name", book_id: nil, created_at: Thu, 25 Feb 2016 20:28:45 UTC +00:00, updated_at: Thu, 25 Feb 2016 20:28:45 UTC +00:00>
recent results bind.pry 27: def genre => 28: binding.pry 29: @genre || = Genre.find_or_initialize_by (name :: name) 30: # @genre || = Genre.find_or_initialize_by (name: params [: genre] [: name]) 31: end