如何使用嵌套资源保存数据?

时间:2019-01-09 10:23:01

标签: ruby-on-rails

我正在Rails上练习,并且在嵌套资源方面遇到困难。 我的数据库中有作者,书籍和类型。我在routes.rb上添加了嵌套资源:

  resources :authors do
    resources :books
  end

我可以通过rake routesnew_author_book看到新路线。

我添加了指向作者的链接-show.html.erb

<%= link_to "Add a Book to This Author" , new_author_book_path(@author) %>

因此,此链接将我重定向到http://localhost:3000/authors/[author_id]/books/new

通过在new.html.erb(Books)中使用params[:author_id],我可以正确看到author_id

我尝试使用以下方法在books_controller中找到该作者:

def create
  @author = Author.find(params[:author_id])
  @book = @author.book.create(book_params)
end

但是错误发生为Couldn't find Author without an ID

我使用脚手架来创建这些表和关系。

这是我的_form.html.erb部分(书籍)

<%= form_with(model: book, local: true) do |form| %>
  <% if book.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(book.errors.count, "error") %> prohibited this book from being saved:</h2>

      <ul>
      <% book.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= form.label :name %>
    <%= form.text_field :name %>
  </div>

  <div class="field">
    <%= form.label :release %>
    <%= form.text_field :release %>
  </div>

  <div class="actions">
    <%= form.submit %>
  </div>
<% end %>

这是books_contollers#new

  def new
    @book = Book.new
  end

应用程序的最新状态:

books_controller.rb :

  def new
    @author = Author.find(params[:author_id])
    @book = @author.books.new
  end

 def create
  @book = Book.create(book_params)
    respond_to do |format|
      if @book.save
        format.html { redirect_to @book, notice: 'Book was successfully created.' }
        format.json { render :show, status: :created, location: @book }
      else
        format.html { render :new }
        format.json { render json: @book.errors, status: :unprocessable_entity }
      end
    end
  end

服务器响应:

Started POST "/authors/11/books" for 127.0.0.1 at 2019-01-10 13:37:21 +0300
Processing by BooksController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"SHORTENED_THIS==", "book"=>{"name"=>"Shiny Tales", "release"=>"2011", "genre_id"=>"1"}, "commit"=>"Create Book", "author_id"=>"11"}
   (0.1ms)  BEGIN
  ↳ app/controllers/books_controller.rb:29
  Genre Load (0.2ms)  SELECT  `genres`.* FROM `genres` WHERE `genres`.`id` = 1 LIMIT 1
  ↳ app/controllers/books_controller.rb:29
   (0.2ms)  ROLLBACK
  ↳ app/controllers/books_controller.rb:29
   (0.1ms)  BEGIN
  ↳ app/controllers/books_controller.rb:31
   (0.1ms)  ROLLBACK
  ↳ app/controllers/books_controller.rb:31
  Rendering books/new.html.erb within layouts/application
  Rendered books/_form.html.erb (1.8ms)
  Rendered books/new.html.erb within layouts/application (2.8ms)
Completed 200 OK in 32ms (Views: 20.0ms | ActiveRecord: 1.3ms)

我试图遵循Rails指南添加第二个模型 部分,但我认为这有点不同。

在这种情况下,我不想进行任何作者选择,因为我已经在author-show.html.erb中选择了一位作者

3 个答案:

答案 0 :(得分:2)

在我看来,这里需要进行一些改组。我相信您的newcreate动作是错误的。

params[:author_id]可用于您的new操作,该操作应类似于:

def new
  @author = Author.find(params[:author_id])
  @book = @author.books.new
end

然后create应该是:

def create
  @book = Book.create(book_params)
end

您很可能需要在book表单中为author_id输入一个字段,尽管我从脑海中回想起这是否有必要。例如:

<%= form.hidden_field :author_id, book.author_id %>

如果这样做,请确保author_id的参数中将books_controllers列入白名单:

def book_params
  params.require(:book).permit(:author_id, ... the rest of your attributes)
end

此外,您的表单应更新为指向嵌套资源(docs here),它将为您的params[:author_id]操作提供create,这意味着您可以保持原样。您只需将数组传递给表单生成器,即

<%= form_with(model: [@author, @book]) do |form| %>

这将设置表单以按需使用author_id参数的嵌套URL。

如果采用这种方法,您的create操作应如下所示:

def create
  @author = Author.find(params[:author_id])
  @book = @author.books.create(book_params)
end

希望那里有帮助-让我知道你的生活。

答案 1 :(得分:0)

1:首先,最干净的方法是:

form_with(model: @author, url: (@book.new_record? ? [@author, @book]: @book))

这将引导您使用author_id创建/更新图书路径。在您的控制器中:

def create
  @author = Author.find(params[:author_id])
  @book = @author.book.create(book_params)
end

2 :否则,您可以在现有表单中添加hidden_​​field,例如:

<%= form.hidden_field :author_id, params[:author_id] %>

并在您的控制器中:

def create
  @book = Book.create(book_params)
end

答案 2 :(得分:0)

最后,我通过在set_author中将自定义函数创建为books_controller来解决了问题

class BooksController < ApplicationController
  before_action :set_book, only: [:show, :edit, :update, :destroy]
  before_action :set_author , only: [:new,:create]

  def new
    @book = Book.new
  end

  def create
    @book = Book.create(book_params)
    @book.author_id = @author.id
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_book
      @book = Book.find(params[:id])
    end

    def set_author
      @author = Author.find(params[:author_id])
    end
end