使用Rails find_or_create_by和表单对象

时间:2016-02-12 00:51:14

标签: ruby-on-rails ruby-on-rails-4 rails-activerecord

我正在创建一个Rails库应用,其中有一种类型has_many书籍和一本书belongs_to一种类型。我需要使用表单对象,因为我的数据模型最终会有多个多对多的关系,因为我想学习如何使用表单对象。我已经将我的表单对象基于Rails Cast#416表单对象。我的表单,表单对象,模型和控制器似乎都可以工作。新书被创建并且它们与流派相关联,但它们都是流派&#34;流派&#34;。我正在使用find_or_create_by。我认为问题在于book_form.rb @genre ||= Genre.find_by(name: :name)其中class Book < ActiveRecord::Base belongs_to :genre before_save :convert_isbn . . . end 实际上没有传递来自表单的类型信息。我的相关代码如下:

Model book.rb

class Genre < ActiveRecord::Base
  has_many :books, dependent: :destroy
  validates_associated :books
end

Model genre.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

book_form.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

books_controller.rb

<%= 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 %>

查看new.html.erb

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

使用Pry gem,我在服务器上创建了一本书:

binding.pry

因此,书籍类型在参数中得到了传递,但我在书本表格中以错误的方式访问它。如果我发表评论@genre,该表单会创建一个新的书籍,其中包含流派&#34; name&#34;而不是流派&#34;神秘&#34;就像我想要的那样。

当我在使用binding.pry时将[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> 键入rails中,我得到了

npm install webpack -g

最新的binding.pry结果         27:def类型      =&GT; 28:binding.pry         29:@genre || = Genre.find_or_initialize_by(name :: name)         30:#@ genre || = Genre.find_or_initialize_by(name:params [:genre] [:name])         31:结束

2 个答案:

答案 0 :(得分:3)

如果您要按其名称查找类型,则需要在方法的选项哈希中传递name属性。

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

更新

变化

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

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

同时在genre.save之前和之后抛出binding.pry!如果你键入@genre,看看你得到了什么,因为你的类型方法应该创建了你刚刚发送的实例变量,其中genre.attributes接受了哈希。

我会在书籍参数上考虑相同但是如果切片工作得很好。

此外,由于您在表单视图中调用了Genre.all,因此您可能不需要在控制器操作中执行此操作,但如果您这样做,请考虑使用对象{{ 1}}在您的表单视图中@genres,而不是因为您在模型上进行了两次所有请求。

collection_select

答案 1 :(得分:0)

您每次都会创建流派设置为流派的流派。你不想create一个类型,你可能只想实例化一个类型(new)。

尝试更改此

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

这样的事情

@genre ||= Genre.new

另外,我建议重新命名字段&#34; genre&#34;在模型类型上的其他东西,如&#34; name&#34;。否则,事情会变得非常混乱。

认为这一行:

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

应该更像:

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

如果您要更改为&#34; name&#34;:

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