Using Rails find_or_create_by with form objects

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

+5
source share
2 answers

If you are going to see a genre by its name, you need to pass the name attribute in the hash options of the method.

 def genre @genre ||= Genre.find_or_create_by(name: :name) end 

UPDATE

change

 genre.attributes = params.slice(:name).permit(:name) 

to

 genre.attributes = { :name => params[:genre][:name] } 

Also throw binding.pry before and after genre.save! and see what happens if you type @genre because your genre method was supposed to create this instance variable with which the parameters from genre.attributes that receive the hash are just passed.

I would consider the same in book parameters, but if a slice works, than a fine.

Also, since you are calling Genre.all in your form view, you probably don't need to do this in the controller actions, but if you do, consider using the @genres object inside your collection_select form view because you are doing the whole request twice on the model.

 @genres = Genre.all 
+3
source

You create a genre with a genre set in the genre every time. You do not want to create genre, you probably just want to create an instance of the genre (using new ).

Try changing this

 @genre ||= Genre.find_or_create_by(genre: :genre) 

to something like this

 @genre ||= Genre.new 

In addition, I would suggest renaming the β€œgenre” field in the model genre to something else, such as β€œname”. Otherwise, everything becomes very confusing.

I think this line:

 collection_select(:genre, :id, Genre.all, :genre, :genre) 

should be more like:

 collection_select(:genre, :genre, Genre.all, :genre, :genre) 

and if you were to go to "name":

 collection_select(:genre, :name, Genre.all, :name, :name) 
0
source

Source: https://habr.com/ru/post/1242824/


All Articles