I'm having issues with creating a form that will save my has_many :through associations. I've successfully saved by posting json, but the forms just won't work for me yet. The request params created by the form submit just won't work out. Any help pointing me to the solution would help me from losing any more time on this. Thanks up front.
EDITED -- Added forms_for attempt and the created params json that doesn't work as well at the bottom --
Json post request params that works:
{
"author": {
"name": "Author Name",
"post_authors_attributes": [
{"post_id":"1"},
{"post_id":"2"},
{"post_id":"3"}
]
}
}
Rails form generated params that don't save.
{
"author": {
"name": "assd",
"post_authors_attributes": [
"",
"2",
"3"
]
}
}
...and the relevant code samples...
Author Model
class Author < ActiveRecord::Base
has_many :post_authors
has_many :posts, :through => :post_authors
accepts_nested_attributes_for :post_authors
end
Post Model (Currently only working on the Author has many Posts, not the reverse)
class Post < ActiveRecord::Base
end
PostAuthor Model
class PostAuthor < ActiveRecord::Base
belongs_to :post
belongs_to :author
end
Author Controller new/create actions
# GET /authors/new
def new
@author = Author.new
@author.post_authors.build
end
# POST /authors
# POST /authors.json
def create
@author = Author.new(params)
respond_to do |format|
if @author.save
format.html { redirect_to @author, notice: 'Author was successfully created.' }
format.json { render :show, status: :created, location: @author }
else
format.html { render :new }
format.json { render json: @author.errors, status: :unprocessable_entity }
end
end
end
authors/_form.html.erb
<%= form_for(@author) do |f| %>
<% if @author.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@author.errors.count, "error") %> prohibited this author from being saved:</h2>
<ul>
<% @author.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<%= collection_select(:author, :post_authors_attributes, Post.all, :id, :title,
{include_blank: false, :selected => @author.posts.map(&:id)},
{:multiple => true}) %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Schema
ActiveRecord::Schema.define(version: 20150120190715) do
create_table "authors", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "post_authors", force: :cascade do |t|
t.integer "post_id"
t.integer "author_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "posts", force: :cascade do |t|
t.string "title"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
EDIT -- ADDED Details -- Just for due dilligence, I've also tried using a fields_for, but it produces even more messed up json that doesn't save to the database. I have no idea where the "0" key comes from. I'm stuck on this, any help would greatly be appreciated.
fields_for
<div class="field">
<%= f.fields_for :post_authors, @author.post_authors do |posts_form| %>
<%= f.label :Posts %><br>
<%= posts_form.collection_select(:post_id, Post.all, :id, :title,
{include_blank: false, :selected => @author.posts.map(&:id)},
{:multiple => true}) %>
<% end %>
</div>
Produced params to_json
{
"author": {
"name": "test",
"post_authors_attributes": {
"0": {
"post_id": [
"",
"1",
"2",
"3"
]
}
}
}
}
For anyone fighting the same kind of issue, I finally managed to get this to work with the following collection_select:
<%= f.collection_select(:feature_ids, Feature.all, :id, :name,
{include_blank: false, :include_hidden => false, :selected => @property.features.map(&:id)},
{:multiple => true}) %>
authors/_form.html.erb
:
<%= fields_for(@author_book) do |ab| %>
<div class="field">
<%= ab.label "All Books" %><br>
<%= collection_select(:books, :id, @all_books, :id, :name, {:selected => @author.books.map(&:id)}, {multiple: true}) %>
</div>
<% end %>
authors_controller.rb
:
class AuthorsController < ApplicationController
before_action :set_author, only: [:show, :edit, :update, :destroy]
# GET /authors
# GET /authors.json
def index
@authors = Author.all
end
# GET /authors/1
# GET /authors/1.json
def show
end
# GET /authors/new
def new
@author = Author.new
get_books
respond_to do |format|
format.html
format.json { render json: @author }
end
end
# GET /authors/1/edit
def edit
get_books
end
# POST /authors
# POST /authors.json
def create
@author = Author.new(author_params)
params[:books][:id].each do |book|
if !book.empty?
@author.authorbooks.build(:book_id => book)
end
end
#binding.pry
respond_to do |format|
if @author.save
format.html { redirect_to @author, notice: 'Author was successfully created.' }
format.json { render :show, status: :created, location: @author }
else
format.html { render :new }
format.json { render json: @author.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /authors/1
# PATCH/PUT /authors/1.json
def update
#binding.pry
respond_to do |format|
if @author.update(author_params)
@author.books = []
params[:books][:id].each do |book|
if !book.empty?
@author.books << Book.find(book)
end
end
format.html { redirect_to @author, notice: 'Author was successfully updated.' }
format.json { render :show, status: :ok, location: @author }
else
format.html { render :edit }
format.json { render json: @author.errors, status: :unprocessable_entity }
end
end
end
# DELETE /authors/1
# DELETE /authors/1.json
def destroy
@author.destroy
respond_to do |format|
format.html { redirect_to authors_url, notice: 'Author was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_author
@author = Author.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def author_params
params.require(:author).permit(:name,:authorbooks_attributes => [:id,:book_ids => []])
end
def get_books
@all_books = Book.all
@author_book = @author.authorbooks.build
end
# def create_params
# params.require(:authorbooks).permit(:author_id,book_id: [])
# end
end
来源:https://stackoverflow.com/questions/28058565/rails-has-many-through-with-collection-select-form-helper