Unpermitted parameters error for Carrierwave in Rails 4 - ruby-on-rails-4

Ok, really stuck here. Have read through all documentation and SO but can't figure out what's going on. Simple case of allowing a User to update their profile by uploading a file using Carrierwave. My log keeps showing an
Unpermitted parameters: avatar
error.
My view:
<%= form_for current_user, html: { multipart: true } do |f| %>
<%= f.file_field :avatar %>
<%= f.submit "Update Profile", remote: true, class: "btn btn-default" %>
<% end %>
my controller:
class UsersController < ApplicationController
def update
current_user.update_attributes(user_params)
respond_to do |format|
format.html { render 'show'}
format.js {}
end
end
def user_params
params.fetch(:user, {}).permit(:id, :email, :username, :password, :password_hash, :avatar, projects_attributes: [:id, :user_id, :name])
end
end
my model
class User < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
end
have not touched the uploader file
class AvatarUploader < CarrierWave::Uploader::Base
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
end
Seems pretty straightforward, why am I getting this error?
UPDATE: Server Log:
Started PATCH "/users/91" for 127.0.0.1 at 2014-04-03 11:38:22 -0700
Processing by UsersController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"+cLF9iPcZSL3Lc87o7/gPZjwgc9ySr7dOKmm6Yxhbno=", "user"=>{"username"=>"s+ Index", "avatar_attributes"=>#<ActionDispatch::Http::UploadedFile:0x007f81db4060c8 #tempfile=#<Tempfile:/var/folders/xl/wpdby5jn59q7hy9w0v2nv8xc0000gn/T/RackMultipart20140403-3890-127m17g>, #original_filename="cover.jpg", #content_type="image/jpeg", #headers="Content-Disposition: form-data; name=\"user[avatar_attributes]\"; filename=\"cover.jpg\"\r\nContent-Type: image/jpeg\r\n">}, "commit"=>"Update Profile", "id"=>"91"}
[1m[35mUser Load (0.4ms)[0m SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 91]]
Unpermitted parameters: avatar

Just had the same problem,
I had a problem because I had fallowed documentation like so:
<%= f.file_field :avatar, multiple: true %>
Changed it to:
<%= form_for #user, multiple: true do |f| %>
in controller:
params.require(:user).permit(:avatar,...)

Related

Remove uploaded images rails 4 carrierwave

Trying to remove uploaded images using carrierwave
<%= f.fields_for :images do |ff| %>
<div class="form-group">
<label>
<%= ff.check_box :remove_image %>
<%= image_tag ff.object.image %>
</label>
</div>
<% end %>
Getting such params in controller
"images_attributes"=>{"0"=>{"remove_image"=>"0", "id"=>"13"}, "1"=>{"remove_image"=>"1", "id"=>"14"}, "2"=>{"remove_image"=>"0", "id"=>"15"}, "3"=>{"remove_image"=>"0", "id"=>"16"}, "4"=>{"remove_image"=>"0", "id"=>"17"}, "5"=>{"remove_image"=>"0", "id"=>"18"}}}
But when updating an object with these params nothing happens, what am I missing?
update
def update
#country = Country.find(params[:id])
if #country.update(country_params)
flash[:notice] = 'Country is successfully updated.'
redirect_to edit_admin_country_path
else
flash[:error] = #country.errors.full_messages[0]
render 'edit'
end
end
def country_params
permitted = [{images_attributes: ["image", "#original_filename", "#content_type", "#headers", "_destroy", "id", "remove_image"]}]
params.require(:country).permit(*permitted)
end
class Country < ActiveRecord::Base
has_many :images
....
end
class Image < ActiveRecord::Base
mount_uploader :image, ImageUploader
belongs_to :country
end
your form looks good but you are missing the controller action
mine looks like:
class ImageController < ApplicationController
...
def update
#image = Image.find(params[:id])
...
if params[:images][:remove_image].present?
#image.remove_image!
end
#image.save
end
end
If you want to remove the file manually, you can call remove_avatar!, then save the object.

Paperclip::AdapterRegistry::NoHandlerError

I recently set up direct image uploads to my Amazon S3 bucket following the tutorial created my Heroku here.
When I upload the file, it is successfully sent to my S3 bucket, but when I try to save the image I get the following error
Paperclip::AdapterRegistry::NoHandlerError at /photos
No handler found for "//mys3bucket.s3.amazonaws.com/uploads/3452345aef45845blabla/the file name.fileExtension"
Here is the form in my view:
<%= bootstrap_form_for #photo, html: { multipart: true, class: 'directUpload', data: { 'form-data' => (#s3_direct_post.fields), 'url' => #s3_direct_post.url, 'host' => URI.parse(#s3_direct_post.url).host } } do |f| %>
<p>
<%= f.text_field :title %>
</p>
<p>
<%= f.file_field :image %>
</p>
<%= f.submit 'Upload', class: 'btn btn-success' %>
<% end %>
Here is my create method in my controller:
def create
if can? :create, Photo
#photo = Photo.new(photo_params)
if #photo.save
redirect_to photos_path
else
render 'new'
end
else
redirect_to root_path
end
end
private
def photo_params
params.require(:photo).permit(:image, :title)
end
def set_s3_direct_post
#s3_direct_post = S3_BUCKET.presigned_post(key: "uploads/#{SecureRandom.uuid}/${filename}", success_action_status: '201', acl: 'public-read')
end
Here is my model:
class Photo < ActiveRecord::Base
has_attached_file :image
belongs_to :article
validates :title, presence: true
validates :image,
attachment_content_type: { content_type: /\Aimage\/.*\Z/ },
attachment_size: { less_than: 5.megabytes }
end
Here is my entire repository
I understand that for some reason Paperclip does not know how to handle the URL but I have no idea how to fix this. I tried adding a new column to my Photos table in my database and just saving the URL to that column. That worked perfectly of course because Paperclip was not involved. Any help would be much appreciated.
Have you tried to add s3_host_name to your configuration?
Like:
config.paperclip_defaults = {
storage: :s3,
s3_host_name: "s3-ap-northeast-1.amazonaws.com", #example
s3_region: ENV['AWS_REGION'],
s3_credentials: {
bucket: ENV['S3_BUCKET_NAME'],
access_key_id: ENV['AWS_ACCESS_KEY_ID'],
secret_access_key: ENV['AWS_SECRET_ACCESS_KEY']
}
}
I would appear that I have found the answer. I don't think it's the best way of doing it because I am essentially bypassing Paperclip but at this point whatever.
I changed my model and database to just accept a url string instead of an attachment with the following code:
/models/photo.rb
class Photo < ActiveRecord::Base
belongs_to :article
validates :title, presence: true
validates :image_url, presence: true, length: { minimum: 5 }
end
First Migration
class RemoveAttatchedImageFromPhotos < ActiveRecord::Migration
def change
remove_column :photos, :attached_image_file_name
remove_column :photos, :attached_image_content_type
remove_column :photos, :attached_image_file_size
remove_column :photos, :attached_image_updated_at
end
end
Second Migration
class AddImageUrlToPhoto2 < ActiveRecord::Migration
def change
add_column :photos, :image_url, :string
end
end

Act-As-Taggable not saving to database

Hi I am attempting to use act_as_taggable ruby gem and have followed the github guide and a recent tutorial as well as checked stack overflow for advice. My code all seems to be correct but when I attempt to save tags via my form partial or via the rails console they do not save to the database.
Below relevant code excerpts.
Thanks in advance for any help!
post.rb
class Post < ActiveRecord::Base
acts_as_taggable
acts_as_taggable_on :tags
validates :title, presence: true, length: {minimum: 5}
validates :body, presence: true
extend FriendlyId
friendly_id :title, use: :slugged
end
post_controller.rb
class PostsController < ApplicationController
before_action :find_post, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
def index
if params[:tag].present?
#posts = Post.tagged_with(params[:tag]).paginate(:page => params[:page], :per_page => 10)
else
#posts = Post.all.order('created_at DESC').paginate(page: params[:page], per_page: 10)
end
end
def new
#post = Post.new
end
def show
end
def create
#post = Post.new(post_params)
if #post.save
redirect_to #post, notice: "Article succesfully saved!"
else
render 'new', notice: "Try Again. I was unable to save your post."
end
end
def edit
end
def update
if #post.update(params[:post].permit(:title, :body))
redirect_to #post, notice: "Article succesfully edited!"
else
render 'edit'
end
end
def destroy
#post.destroy
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:title, :body, :slug, :tag_list => [])
end
def find_post
#post = Post.friendly.find(params[:id])
end
end
routes.rb
Rails.application.routes.draw do
devise_for :users, :skip => :registrations
root 'pages#home'
resources :posts
resources :portfolios
get 'tags/:tag', to: 'posts#index', as: :tag
end
_form.html.erb
<%= form_for #post do |f| %>
<% if #post.errors.any? %>
<div id="errors">
<h2><%= pluralize(#post.errors.count, "error") %> prevented this post from saving:</h2>
<ul>
<% #post.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.label :title %><br>
<%= f.text_field :title, class: 'form-control form-control-lg' %><br>
<br>
<%= f.label :body %><br>
<%= f.text_area :body, class: 'form-control form-control-lg', :rows => "10" %><br>
<br>
<%= f.label :tag_list, "Tags (separated by commas)" %>
<%= f.text_field :tag_list, class: 'form-control' %>
<br>
<%= f.submit %>
<% end %>
excerpt from show.html.erb
<% #post.tags.any? %>
<% #post.tags.each do |tag| %>
<li><a href="#">
<%= link_to tag.name, tag_path(tag.name) %>
</a></li>
<% end %>
Params log for _form as requested by Pavan:
Started PATCH "/posts/my-third-post-with-tags" for ::1 at 2015-09-23 15:56:36 +1000
Processing by PostsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"GwLMImKSDyhPt77nI+fhIkF4QVsFxMen851wjcVorrxOCdU+TcRKIZv1im4/oNx7DERYCI91vE9YDjvSda7MAg==", "post"=>{"title"=>"My third post with tags", "body"=>"to update your local repository to the newest commit, execute\r\ngit pull\r\nin your working directory to fetch and merge remote changes.\r\nto merge another branch into your active branch (e.g. master), use\r\ngit merge <branch>\r\nin both cases git tries to auto-merge changes. Unfortunately, this is not always possible and results in conflicts. You are responsible to merge those conflicts manually by editing the files shown by git. After changing, you need to mark them as merged with\r\ngit add <filename>\r\nbefore merging changes, you can also preview them by using\r\ngit diff <source_branch> <target_branch>", "tag_list"=>"tag, css"}, "commit"=>"Update Post", "id"=>"my-third-post-with-tags"}
Post Load (0.1ms) SELECT "posts".* FROM "posts" WHERE "posts"."slug" = ? ORDER BY "posts"."id" ASC LIMIT 1 [["slug", "my-third-post-with-tags"]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT 1 [["id", 1]]
Unpermitted parameter: tag_list
(0.0ms) begin transaction
(0.0ms) commit transaction
Redirected to http://localhost:3000/posts/my-third-post-with-tags
Completed 302 Found in 4ms (ActiveRecord: 0.3ms)
Started GET "/posts/my-third-post-with-tags" for ::1 at 2015-09-23 15:56:36 +1000
Processing by PostsController#show as HTML
Parameters: {"id"=>"my-third-post-with-tags"}
Post Load (0.1ms) SELECT "posts".* FROM "posts" WHERE "posts"."slug" = ? ORDER BY "posts"."id" ASC LIMIT 1 [["slug", "my-third-post-with-tags"]]
ActsAsTaggableOn::Tag Exists (0.1ms) SELECT 1 AS one FROM "tags" INNER JOIN "taggings" ON "tags"."id" = "taggings"."tag_id" WHERE "taggings"."taggable_id" = ? AND "taggings"."taggable_type" = ? AND "taggings"."context" = ? LIMIT 1 [["taggable_id", 2], ["taggable_type", "Post"], ["context", "tags"]]
ActsAsTaggableOn::Tag Load (0.1ms) SELECT "tags".* FROM "tags" INNER JOIN "taggings" ON "tags"."id" = "taggings"."tag_id" WHERE "taggings"."taggable_id" = ? AND "taggings"."taggable_type" = ? AND "taggings"."context" = ? [["taggable_id", 2], ["taggable_type", "Post"], ["context", "tags"]]
Rendered posts/show.html.erb within layouts/application (3.2ms)
Rendered layouts/_header.html.erb (496.3ms)
Rendered devise/shared/_links.html.erb (0.2ms)
Rendered devise/sessions/_new.html.erb (4.6ms)
Rendered layouts/_slideoutpanels.html.erb (460.2ms)
Rendered layouts/_footer.html.erb (0.0ms)
Completed 200 OK in 1486ms (Views: 1484.3ms | ActiveRecord: 0.2ms)
Your error is from PostsController#update as HTML
In controller you don;t permit tag_list
#post.update(params[:post].permit(:title, :body))

Unable to save Join Table details to database in Rails 4

I've always had trouble with has_many :through relationships on join tables and am stuck again on where I'm going wrong. I think it might be in my controller as I'm still getting to grips with how it all works.
I have three models:
class Role < ActiveRecord::Base
has_many :assignments, inverse_of: :role
has_many :employees, :through => :assignments
accepts_nested_attributes_for :assignments
end
class Employee < ActiveRecord::Base
has_many :assignments, inverse_of: :employee
has_many :roles, :through => :assignments
accepts_nested_attributes_for :assignments
end
class Assignment < ActiveRecord::Base
belongs_to :employee, inverse_of: :assignment
belongs_to :role, inverse_of: :assignment
accepts_nested_attributes_for :employee
accepts_nested_attributes_for :role
end
I'd like to be able to set the pre-created role for an employee when creating or editing an employee. My New Employee form is as follows:
<%= semantic_form_for #employee do |f| %>
<%= render 'shared/error_messages' %>
<%= f.inputs do %>
<%= f.input :first_name %>
<%= f.input :last_name %>
<%= f.input :email %>
<%= f.input :password %>
<%= f.input :password_confirmation %>
<%= semantic_fields_for :roles do |role| %>
<%= role.input :role, :as => :select, :collection => Role.all %>
<%= role.semantic_fields_for :assignments do |assignment| %>
<%= assignment.input :start_date, :as => :date_select %>
<%= assignment.input :end_date, :as => :date_select %>
<%= assignment.input :assignment_no %>
<%= assignment.input :assignment_id %>
<% end %>
<% end %>
<% end %>
<%= f.actions do %>
<%= f.action :submit, :as => :button %>
<%= f.action :cancel, :as => :link %>
<% end %>
<% end %>
and finally my Employee Controller is:
class EmployeesController < ApplicationController
before_action :set_employee, only: [:show, :edit, :update, :destroy]
def index
#employees = Employee.paginate(page: params[:page])
end
def show
end
def new
#employee = Employee.new
role = #employee.roles.build
end
def edit
#employee = Employee.find(params[:id])
end
def create
#employee = Employee.new(employee_params)
if #employee.save
#employee.send_activation_email
flash[:info] = "Please check your email to activate your account."
redirect_to root_url
else
render 'new'
end
end
def update
respond_to do |format|
if #employee.update(employee_params)
flash[:success] = "Profile updated"
format.html { redirect_to #employee, notice: 'Employee was successfully updated.' }
format.json { render :show, status: :ok, location: #employee }
else
format.html { render :edit }
format.json { render json: #employee.errors, status: :unprocessable_entity }
end
end
end
def destroy
Employee.find(params[:id]).destroy
flash[:success] = "Employee deleted"
respond_to do |format|
format.html { redirect_to employees_url, notice: 'Employee was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_employee
#employee = Employee.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def employee_params
params.require(:employee).permit(:avatar, :email, :first_name, :last_name, :password, :assignments_attributes => [:employee_id, :role_id, :roles_attributes => [:id, :employee_id, :PID]])
end
end
I appreciate that this subject is always on Stackoverflow, but I can't seem to translate any of the problems into where I am. Any help would be greatly appreciated!
The console output is as follows:
Started POST "/employees" for 127.0.0.1 at 2015-05-16 18:05:34 +0200
source=rack-timeout id=d5933405c94d9c2e8bca3332564528ec timeout=60000ms service=25ms state=active
Processing by EmployeesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"TgHHELkt2zXUtS474WwGeHcmfKKw/broHxRdhlsF1P4JyGoa+03rchb6mfxbOSXKdrPgcJMeBCyIlCMHqPlQBA==", "employee"=>{"first_name"=>"John", "last_name"=>"Smith", "email"=>"john#smith.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "roles"=>{"role"=>"2", "assignments"=>{"start_date(1i)"=>"2012", "start_date(2i)"=>"7", "start_date(3i)"=>"5", "end_date(1i)"=>"2016", "end_date(2i)"=>"3", "end_date(3i)"=>"1", "assignment_no"=>"4", "assignment_id"=>"A3392822"}}, "button"=>""}
Unpermitted parameter: password_confirmation
(0.2ms) BEGIN
source=rack-timeout id=d5933405c94d9c2e8bca3332564528ec timeout=60000ms service=1113ms state=active
SQL (70.3ms) INSERT INTO "employees" ("email", "first_name", "last_name", "password_digest", "created_at", "updated_at", "activation_digest") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id" [["email", "john#smith.com"], ["first_name", "John"], ["last_name", "Smith"], ["password_digest", "$2a$10$g3PNppheVZ8AFnnKuWg6secBdGev0NlCXjUx.RXsky03Xl9L3CubO"], ["created_at", "2015-05-16 16:05:35.422312"], ["updated_at", "2015-05-16 16:05:35.422312"], ["activation_digest", "$2a$10$L0yFuvmlJon7fWod6lHj..O7yDRaqwqTlTkJgD7Evqx.dA4pDTlBC"]]
(133.0ms) COMMIT
(I'm working on a very slow computer!)

Rails 4 multiple image or file upload using carrierwave

How can I upload multiple images from a file selection window using Rails 4 and CarrierWave? I have a post_controller and post_attachments model. How can I do this?
Can someone provide an example? Is there a simple approach to this?
This is solution to upload multiple images using carrierwave in rails 4 from scratch
Or you can find working demo :
Multiple Attachment Rails 4
To do just follow these steps.
rails new multiple_image_upload_carrierwave
In gem file
gem 'carrierwave'
bundle install
rails generate uploader Avatar
Create post scaffold
rails generate scaffold post title:string
Create post_attachment scaffold
rails generate scaffold post_attachment post_id:integer avatar:string
rake db:migrate
In post.rb
class Post < ActiveRecord::Base
has_many :post_attachments
accepts_nested_attributes_for :post_attachments
end
In post_attachment.rb
class PostAttachment < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
belongs_to :post
end
In post_controller.rb
def show
#post_attachments = #post.post_attachments.all
end
def new
#post = Post.new
#post_attachment = #post.post_attachments.build
end
def create
#post = Post.new(post_params)
respond_to do |format|
if #post.save
params[:post_attachments]['avatar'].each do |a|
#post_attachment = #post.post_attachments.create!(:avatar => a)
end
format.html { redirect_to #post, notice: 'Post was successfully created.' }
else
format.html { render action: 'new' }
end
end
end
private
def post_params
params.require(:post).permit(:title, post_attachments_attributes: [:id, :post_id, :avatar])
end
In views/posts/_form.html.erb
<%= form_for(#post, :html => { :multipart => true }) do |f| %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<%= f.fields_for :post_attachments do |p| %>
<div class="field">
<%= p.label :avatar %><br>
<%= p.file_field :avatar, :multiple => true, name: "post_attachments[avatar][]" %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
To edit an attachment and list of attachment for any post.
In views/posts/show.html.erb
<p id="notice"><%= notice %></p>
<p>
<strong>Title:</strong>
<%= #post.title %>
</p>
<% #post_attachments.each do |p| %>
<%= image_tag p.avatar_url %>
<%= link_to "Edit Attachment", edit_post_attachment_path(p) %>
<% end %>
<%= link_to 'Edit', edit_post_path(#post) %> |
<%= link_to 'Back', posts_path %>
Update form to edit an attachment views/post_attachments/_form.html.erb
<%= image_tag #post_attachment.avatar %>
<%= form_for(#post_attachment) do |f| %>
<div class="field">
<%= f.label :avatar %><br>
<%= f.file_field :avatar %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Modify update method in post_attachment_controller.rb
def update
respond_to do |format|
if #post_attachment.update(post_attachment_params)
format.html { redirect_to #post_attachment.post, notice: 'Post attachment was successfully updated.' }
end
end
end
In rails 3 no need to define strong parameters and as you can define attribute_accessible in both the model and accept_nested_attribute to post model because attribute accessible is deprecated in rails 4.
For edit an attachment we cant modify all the attachments at a time. so we will replace attachment one by one, or you can modify as per your rule, Here I just show you how to update any attachment.
If we take a look at CarrierWave's documentation, this is actually very easy now.
https://github.com/carrierwaveuploader/carrierwave/blob/master/README.md#multiple-file-uploads
I will use Product as the model I want to add the pictures, as an example.
Get the master branch Carrierwave and add it to your Gemfile:
gem 'carrierwave', github:'carrierwaveuploader/carrierwave'
Create a column in the intended model to host an array of images:
rails generate migration AddPicturesToProducts pictures:json
Run the migration
bundle exec rake db:migrate
Add pictures to model Product
app/models/product.rb
class Product < ActiveRecord::Base
validates :name, presence: true
mount_uploaders :pictures, PictureUploader
end
Add pictures to strong params in ProductsController
app/controllers/products_controller.rb
def product_params
params.require(:product).permit(:name, pictures: [])
end
Allow your form to accept multiple pictures
app/views/products/new.html.erb
# notice 'html: { multipart: true }'
<%= form_for #product, html: { multipart: true } do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
# notice 'multiple: true'
<%= f.label :pictures %>
<%= f.file_field :pictures, multiple: true, accept: "image/jpeg, image/jpg, image/gif, image/png" %>
<%= f.submit "Submit" %>
<% end %>
In your views, you can reference the images parsing the pictures array:
#product.pictures[1].url
If you choose several images from a folder, the order will be the exact order you are taking them from top to bottom.
Some minor additions to the SSR answer:
accepts_nested_attributes_for does not require you to change the parent object's controller. So if to correct
name: "post_attachments[avatar][]"
to
name: "post[post_attachments_attributes][][avatar]"
then all these controller changes like these become redundant:
params[:post_attachments]['avatar'].each do |a|
#post_attachment = #post.post_attachments.create!(:avatar => a)
end
Also you should add PostAttachment.new to the parent object form:
In views/posts/_form.html.erb
<%= f.fields_for :post_attachments, PostAttachment.new do |ff| %>
<div class="field">
<%= ff.label :avatar %><br>
<%= ff.file_field :avatar, :multiple => true, name: "post[post_attachments_attributes][][avatar]" %>
</div>
<% end %>
This would make redundant this change in the parent's controller:
#post_attachment = #post.post_attachments.build
For more info see Rails fields_for form not showing up, nested form
If you use Rails 5, then change Rails.application.config.active_record.belongs_to_required_by_default value from true to false (in config/initializers/new_framework_defaults.rb) due to a bug inside accepts_nested_attributes_for (otherwise accepts_nested_attributes_for won't generally work under Rails 5).
EDIT 1:
To add about destroy:
In models/post.rb
class Post < ApplicationRecord
...
accepts_nested_attributes_for :post_attachments, allow_destroy: true
end
In views/posts/_form.html.erb
<% f.object.post_attachments.each do |post_attachment| %>
<% if post_attachment.id %>
<%
post_attachments_delete_params =
{
post:
{
post_attachments_attributes: { id: post_attachment.id, _destroy: true }
}
}
%>
<%= link_to "Delete", post_path(f.object.id, post_attachments_delete_params), method: :patch, data: { confirm: 'Are you sure?' } %>
<br><br>
<% end %>
<% end %>
This way you simply do not need to have a child object's controller at all! I mean no any PostAttachmentsController is needed anymore. As for parent object's controller (PostController), you also almost don't change it - the only thing you change in there is the list of the whitelisted params (to include the child object-related params) like this:
def post_params
params.require(:post).permit(:title, :text,
post_attachments_attributes: ["avatar", "#original_filename", "#content_type", "#headers", "_destroy", "id"])
end
That's why the accepts_nested_attributes_for is so amazing.
Also I figured out how to update the multiple file upload and I also refactored it a bit. This code is mine but you get the drift.
def create
#motherboard = Motherboard.new(motherboard_params)
if #motherboard.save
save_attachments if params[:motherboard_attachments]
redirect_to #motherboard, notice: 'Motherboard was successfully created.'
else
render :new
end
end
def update
update_attachments if params[:motherboard_attachments]
if #motherboard.update(motherboard_params)
redirect_to #motherboard, notice: 'Motherboard was successfully updated.'
else
render :edit
end
end
private
def save_attachments
params[:motherboard_attachments]['photo'].each do |photo|
#motherboard_attachment = #motherboard.motherboard_attachments.create!(:photo => photo)
end
end
def update_attachments
#motherboard.motherboard_attachments.each(&:destroy) if #motherboard.motherboard_attachments.present?
params[:motherboard_attachments]['photo'].each do |photo|
#motherboard_attachment = #motherboard.motherboard_attachments.create!(:photo => photo)
end
end
Here is my second refactor into the model:
Move private methods to model.
Replace #motherboard with self.
Controller:
def create
#motherboard = Motherboard.new(motherboard_params)
if #motherboard.save
#motherboard.save_attachments(params) if params[:motherboard_attachments]
redirect_to #motherboard, notice: 'Motherboard was successfully created.'
else
render :new
end
end
def update
#motherboard.update_attachments(params) if params[:motherboard_attachments]
if #motherboard.update(motherboard_params)
redirect_to #motherboard, notice: 'Motherboard was successfully updated.'
else
render :edit
end
end
In motherboard model:
def save_attachments(params)
params[:motherboard_attachments]['photo'].each do |photo|
self.motherboard_attachments.create!(:photo => photo)
end
end
def update_attachments(params)
self.motherboard_attachments.each(&:destroy) if self.motherboard_attachments.present?
params[:motherboard_attachments]['photo'].each do |photo|
self.motherboard_attachments.create!(:photo => photo)
end
end
When using the association #post.post_attachments you do not need to set the post_id.