Reputation: 15
I have one model "Breads" that has_many "Posts". I would like to have a form to create a new "Post" on the 'show' page for a given "Bread" that creates the association to the record of 'Bread' which the 'show' page is displaying.
I have tried a few different methods, but all are giving an error. The method that I have shown below gives a "Association cannot be used in forms not associated with an object" error.
/views/breads/show.html.erb:
<p>
<strong>Bread Type:</strong>
<%= @bread.bread_type %>
</p>
<table>
<tr>
<th>Uploaded By</th>
<th>Comment</th>
<th>Picture</th>
</tr>
<% @bread.posts.each do |post| %>
<tr>
<td><%= post.uploader %></td>
<td><%= post.comment %></td>
<td><%= image_tag post.attachment_url.to_s %></td>
</tr>
<% end %>
</table>
<%= @bread.id %>
<%= simple_form_for @bread do |b| %>
<%= simple_fields_for :posts do |p| %>
<%= p.input :uploader %>
<%= p.input :comment %>
<%= p.association :bread, value: @bread.id %>
<%= p.file_field :attachment %><br>
<%= p.button :submit %>
<% end %>
<% end %>
<%= link_to 'Back', breads_path %>
config/routes.rb
Rails.application.routes.draw do
get 'welcome/index'
root 'welcome#index'
resources :breads
resources :posts
end
controllers/breads_controller.rb:
class BreadsController < ApplicationController
def index
@breads = Bread.all
end
def show
@bread = Bread.find(params[:id])
end
def new
@bread = Bread.new
end
def edit
@bread = Bread.find(params[:id])
end
def create
@bread = Bread.new(bread_params)
if @bread.save
redirect_to @bread
else
render 'new'
end
end
def update
@bread = Bread.find(params[:id])
if @bread.update(bread_params)
redirect_to @bread
else
render 'edit'
end
end
def destroy
@bread = Bread.find(params[:id])
@bread.destroy
redirect_to breads_path
end
private
def bread_params
params.require(:bread).permit(:bread_type)
end
end
models/bread.rb:
class Bread < ActiveRecord::Base
has_many :posts
validates :bread_type, presence: true, uniqueness: true
end
models/post.rb:
class Post < ActiveRecord::Base
belongs_to :bread
mount_uploader :attachment, AttachmentUploader
end
Upvotes: 1
Views: 307
Reputation: 1535
Do this -
<%= simple_form_for @bread do |b| %>
<%= b.simple_fields_for(:posts,@bread.posts.build) do |p| %>
<%= p.input :uploader %>
<%= p.input :comment %>
<%= p.file_field :attachment %><br>
<%= p.button :submit %>
<% end %>
<% end %>
and make changes in beard_params
def beard_params
params.require(:bread).permit!
end
Here permit!
requires all parameters and for other way you can use @pawan's answer.
Upvotes: 1
Reputation: 33542
Extending @Amit Suroliya answer, you need to add posts_attributes
to bread_params
def bread_params
params.require(:bread).permit(:id, :bread_type, posts_attributes: [:id, :uploader, :comment, :bread_id, :attachment])
end
Update:
You also need to add accepts_nested_attributes_for :posts
in Bread model.
Upvotes: 0
Reputation: 808
Iam sorry, but this is not good way at all, try to don't abuse rails and rest routes :)
Here is easy example how to do that:
config/routes.rb
resources :bread do
resources :posts
end
This means there will be routes like:
bin/rake routes
breads - breads#index
bread/:id - breads#show
etc..
and most important
bread/:bread_id/posts/:id
...
That means posts are nested resources for bread...
app/controllers/breads_controller.rb
controller BreadsController < BaseController
before_action :find_bread, except: %i(index create new)
.... action new, update, edit etc..
end
but now its the important part in PostsController..
app/controllers/posts_controller.rb
controller PostsController < BaseController
before_action :find_bread
before_action :find_post, except: %i(index new create)
before_action :build_post, only: %i(new create)
.... action new, update, edit etc..
# Example with :return link
def create
if @post.save
if params[:back] == 'bread_show'
redirect_to bread_path(@bread)
else
redirect_to bread_post_path(@bread, @post)
end
else
render 'new'
end
end
private
def build_post
if params[:post]
@post = @bread.posts.build(post_params)
else
@post = @bread.posts.build
end
end
def find_post
@post = @bread.posts.find(params[:id])
end
def find_bread
@bread = Bread.find(params[:bread_id])
end
... post params ...
end
Now you have rest full routes and you're able to do what you want without such a pain and clean
... output hidden
<%= @bread.id %>
<%= simple_form_for @bread.posts.build do |b| %>
<%= p.input :uploader %>
<%= p.input :comment %>
<%= p.file_field :attachment %><br>
<%# Send back link to return on proper page %>
<%= p.hidden_field :back, 'bread_show' %>
<%= p.button :submit %>
<% end %>
<%= link_to 'Back', breads_path %>
There can be some mistakes, I write this code from memory, can't try that :(
Upvotes: 0