Unpermitted parameter: :images with dropzonejs
dcalixto opened this issue · comments
after setup a simple multiple file upload between item and photos with nested routes
resources :items, except: [:index] do
resources :photos, only: %i[create destroy]
end
when i try to update the photos item, someone know why this?
Unpermitted parameter: :images. Context: { controller: PhotosController, action: create, request: #<ActionDispatch::Request:0x00007fc6d4ac9688>, params: {"item"=>{"images"=>{"0"=>#<ActionDispatch::Http::UploadedFile:0x00007fc6d4ae4690 @tempfile=#<Tempfile:/tmp/RackMultipart20230824-17259-174kltg.jpg>, @content_type="image/jpeg", @original_filename="caraguatatuba.jpg", @headers="Content-Disposition: form-data; name=\"item[images][0]\"; filename=\"caraguatatuba.jpg\"\r\nContent-Type: image/jpeg\r\n">, "1"=>#<ActionDispatch::Http::UploadedFile:0x00007fc6d4ae4618 @tempfile=#<Tempfile:/tmp/RackMultipart20230824-17259-1u4m5ml.jpg>, @content_type="image/jpeg", @original_filename="background2.jpg", @headers="Content-Disposition: form-data; name=\"item[images][1]\"; filename=\"background2.jpg\"\r\nContent-Type: image/jpeg\r\n">}}, "controller"=>"photos", "action"=>"create", "item_id"=>"130"} }
the dropzone edit form has a file field inside the dropzone fallback container
<div class="fallback">
<%= f.file_field 'item[images]', multiple: true %>
</div>
and the item and photos controller are using the standard methods
class PhotosController < ApplicationController
skip_before_action :authenticate_user!, except: %i[create destroy]
def create
@item = Item.find(params[:item_id])
@photo = @item.photos.build(images_params)
if @photo.save
redirect_to edit_item_url(@item), notice: 'Photos uploaded successfully'
else
render 'items/edit' # Redisplay the form with errors
end
end
def destroy
@item = Item.find(params[:item_id]) # Make sure you're setting @item correctly
remove_image_at_index(params[:id].to_i)
flash[:error] = 'Failed deleting image' unless @item.save
redirect_back(fallback_location: item_path(@item))
end
private
def find_item
@item = Item.find(params[:item_id])
end
def photo_params
params.require(:photo).permit(:image)
end
def remove_image_at_index(index)
remain_images = @item.photos.to_a # copy the array
deleted_image = remain_images.delete_at(index) # delete the target image
deleted_image.try(:destroy!) # delete image from S3
@item.photos = remain_images # re-assign back
@item.photos.remove_images = true if remain_images.length.zero?
end
def images_params
params.require(:item).permit(images: [])
# look into format which comes, may be here will be image: {...}
# allow nested params as array
end
end
class ItemsController < ApplicationController
def edit
@item = Item.friendly.find(params[:id])
@photos = @item.photos
end
def update
if @item.update(item_params)
@item.published_at = Time.now
if params[:item][:images]
params[:item][:images].each do |image|
Photo.create(item: @item, image: image)
end
end
end
redirect_to item_url(@item), notice: 'O item foi atualizado'
else
render :edit, status: :unprocessable_entity
end
end
the dropzone
$(function(){
var post_url = "/items/<%= @item.id %>/photos";
var myDropzone = new Dropzone("#myDropzone", {
url: post_url,
//url: "/photos",
method: "POST",
paramName: "item[images]",
uploadMultiple: true,
autoProcessQueue: true,
addRemoveLinks: true,
parallelUploads: 6,
maxFiles: 6,
removedfile: function(file){
var _ref,
item_id = file.item_id,
image_index = file.index,
url = "/items/" + item_id + "/photos/" + image_index;
$.ajax({
type: 'DELETE',
url: url
});
return (_ref = file.previewElement) != null ? _ref.parentNode.removeChild(file.previewElement) : void 0;
},
headers: {
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')},
init: function() {
var myDropzone = this;
$("input[type='submit']").on("click", function(e) {
if (myDropzone.getQueuedFiles().length > 0) {
e.preventDefault();
e.stopPropagation();
myDropzone.processQueue();
return;
}
});
this.on("successmultiple", function(files, response) {
window.location.href = '<%= edit_item_url(@item) %>'
});
}
});
<% @photos.each_with_index do |image, index| %>
var mockFile = {
name: "<%= image.image.id %>",
size: "<%= image.image.size%>",
index: <%= index %>,
item_id : <%= @item.id %>
}
myDropzone.files.push(mockFile);
myDropzone.emit("addedfile", mockFile);
myDropzone.options.thumbnail.call(myDropzone, mockFile,"<%= image.image_url(:medium) %>");
// myDropzone.createThumbnailFromUrl(mockFile, "<#%= image.url %>");
myDropzone.emit("complete", mockFile);
<% end %>
});
Very hard to debug this.
Try the following:
def images_params
params.require(:item).permit(images: {}) # <---- notice the curly bracked
end
https://apidock.com/rails/ActiveRecord/NestedAttributes/ClassMethods/accepts_nested_attributes_for
class Photo < ActiveRecord
has_many :images # is this what you are trying for?
accepts_nested_attributes_for :images # not sure if this is what you want.
end
@benkoshy thank you but the item has many photos!
and the photo has image attachment
class Photo < ApplicationRecord
include ImageUploader::Attachment(:image)
belongs_to :item
validates :image, presence: true
end
and item
class Item < ApplicationRecord
has_many :photos, dependent: :destroy
accepts_nested_attributes_for :photos, allow_destroy: true, reject_if: proc { |attributes|
attributes['image'].blank? && attributes['id'].blank?
}
end
It is very hard to debug like this: but I would prefer something along the following linkes:
def item_params
params.require(:item).permit(photo_attributes: {}) # use the hash
end
It looks like you forgot to permit some parameters. This is not a bug in Shrine, because Shrine doesn't permit parameters for you.