Rails 4:在创建模型时能够 select 一个模型的多个实例,它具有 has_many :through 关系

Rails 4: Being able to select multiple instances of one model when creating the model it has a has_many :through relationship with

当我向我的 Library 添加新的 Book 时,我希望能够指定 Shelves (注意: " Shelf" 是我在我的应用程序中用于将 Books 分组在 Library 中的模型的术语;该模型的更好名称应该是 CategoryGenre,但我很笨)在那个 Library 它应该属于。现在,我可以将 Book 添加到我的 Library,但我遇到了 Shelves 的问题。我在学习方面还很陌生 Rails,如果有任何帮助,我将不胜感激。

型号

user.rb:

class User < ActiveRecord::Base
  has_one :library, dependent: :destroy
  ...
end

library.rb:

class Library < ActiveRecord::Base
  belongs_to :user
  has_many :shelves, dependent: :destroy

  has_many :catalogs, dependent: :destroy
  has_many :books, :through => :catalogs, dependent: :destroy
  ...
end

book.rb:

class Book < ActiveRecord::Base
  has_many :catalogs, dependent: :destroy
  has_many :libraries, :through => :catalogs, dependent: :destroy

  has_many :bookshelves, dependent: :destroy
  has_many :shelves, :through => :bookshelves, dependent: :destroy
  ...
end

catalog.rb:

class Catalog < ActiveRecord::Base
  belongs_to :book
  belongs_to :library
end

shelf.rb:

class Shelf < ActiveRecord::Base
  belongs_to :library
  has_many :bookshelves, dependent: :destroy
  has_many :books, :through => :bookshelves, dependent: :destroy
  ...
end

本书shelf.rb

class Bookshelf < ActiveRecord::Base
  belongs_to :book
  belongs_to :shelf
end

控制器

books_controller.rb

class BooksController < ApplicationController
  ...

  def create
    @book = Book.new(book_params)
    @library = current_user.library

    if @book.save
      @book.catalogs.create(:library_id => @library.id)
      flash[:success] = "Book added to library!"
      redirect_to current_user
    else
      render 'current_user'
    end
  end

  ...

  private
    def book_params
      params.require(:book).permit(:title, :author, :publisher, :isbn)
    end

    ...
end

查看

_book_form.html.erb:

<%= form_for(@book) do |f| %>
  <%= render 'shared/error_messages', object: f.object %>
  <div class="field">
    <%= f.text_field :title, placeholder: "Book Title" %>
    <%= f.text_field :author, placeholder: "Author" %>
    <%= f.text_field :publisher, placeholder: "Publisher" %>
    <%= f.text_field :isbn, placeholder: "ISBN" %>

    <%= f.fields_for :catalogs do |ff| %>
      <%= ff.hidden_field :library_id %>
    <% end %>

    <%= f.fields_for :bookshelves do |ff| %>
      <%= ff.collection_select :shelf_ids, current_user.library.shelves.all.collect {|x| [x.name, x.id]}, {}, multiple: true %>
    <% end %>
  </div>
  <%= f.submit "Add Book to Library", class: "btn btn-primary" %>
<% end %>

在您的 book_params 方法中,您没有列出 shelf_ids。所以它不会被传递到 .new 方法,因此它不会被保存。因为你不想多个 ids,所以你想这样做:

def book_params
  params.require(:book).permit(:title, :author, :publisher, :isbn, shelf_ids: [])
end

这样,控制器就知道它是一个数组,而不仅仅是一个值。

哦,顺便说一句,shelf_ids 和任何其他类似的数组值 必须 放在末尾,否则会引发错误。

编辑:

同时更改这部分代码:

<%= f.fields_for :bookshelves do |ff| %>
  <%= ff.collection_select :shelf_ids, current_user.library.shelves.all.collect {|x| [x.name, x.id]}, {}, multiple: true %>
<% end %>

对此:

<%= f.collection_select :shelf_ids, current_user.library.shelves.all.collect {|x| [x.name, x.id]}, {}, multiple: true %>

您无需担心关联的 through 部分。只需设置 has_many 部分即可。