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
Related
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.
I'm using the sign-up and login that we built with the rails tutorial as a base for a Reddit clone that I’m making. As it stands the application is functioning properly apart from the user_id in comments table is blank when I make a comment, the link_id is present and correct so I can make comments on a link. The user_id in links table is also present and correct.
I'm fairly certain that the error i've made is in the create action of comments_controller.rb but it could also be my original migration. What's confusing me (as a novice) is I had this working in it's current form once before with rails 4.1.8 and device. However, using this approach with rails 4.2.1 using the rails tutorial as a base, it doesn't work. I'm a bit new here so I hope i've formulated the post correctly and given enough information so somebody could give me some pointers as to the problem
Comment Controller
before_action :logged_in_user, only: [:create, :destroy]
def create
#link = Link.find(params[:link_id])
#comment = #link.comments.create(params[:comment].permit(:link_id, :body))
#comment.user = User.find(current_user.id)
redirect_to link_path(#link)
end
def destroy
#link = Link.find(params[:link_id])
#comment = #link.comments.find(params[:id])
#comment.destroy
redirect_to link_path(#link)
end
private
def logged_in_user
unless logged_in?
flash[:danger] = "Please log in."
redirect_to login_url
end
end
Form
app/views/links/show.html.erb
<h2 class="comment-count"><%= pluralize(#link.comments.count, "comment") %></h2>
<%= render #link.comments %>
<%= form_for([#link, #link.comments.build]) do |f| %>
<%= render 'comments/fields', f: f %>
<%= f.submit "Save Comment", class: 'btn btn-primary margin-bottom-10' %>
<% end %>
Partials
app/views/comments/_comment.html.erb
<p class="comment_body"><%= comment.body %></p>
<p class="comment_time"><%= time_ago_in_words(comment.created_at) %> Ago </p>
app/views/comments/_fields.html.erb
<%= render 'shared/comment_error_messages' %>
<%= f.label :body %>
<%= f.text_area :body, class: 'form-control' %>
Routes
config/routes.rb
resources :links do
member do
put "like", to: "links#upvote"
put "dislike", to: "links#downvote"
end
resources :comments
end
Models
app/models/link.rb
belongs_to :user
has_many :comments, dependent: :destroy
app/models/comment.rb
belongs_to :user
belongs_to :link
validates :body, presence: true
app/models/user.rb
has_many :links, dependent: :destroy
Migration
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.integer :link_id
t.text :body
t.references :user, index: true, foreign_key: true
t.timestamps null: false
end
add_index :comments, :link_id
end
end
Hopefully that’s a good enough post and I’ve included everything you need so somebody can help me out, It's a rookie error but i can't see it. thanks in advance.
Regards
As it stands, you are currently creating a comment without saving user_id for it. Try the below code
#comments_controller.rb
def create
#link = Link.find(params[:link_id])
#comment = #link.comments.new(params[:comment].permit(:link_id, :body))
#comment.user = User.find(current_user.id)
#comment.save
redirect_to link_path(#link)
end
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,...)
I am trying to update a nested form. The client table is updated successfully but the location table isn't update. Instead of its creating a new location. Do you guys have any solution ? I've already spend one day on it.
My models:
class Client < ActiveRecord::Base
has_many :locations, :dependent => :destroy
accepts_nested_attributes_for :locations, :allow_destroy => true, :update_only => true
end
class Location < ActiveRecord::Base
belongs_to :client
end
Selectcom::Application.routes.draw do
resources :clients
end
My Controller:
class ClientsController < ApplicationController
before_action :set_client, only: [:edit, :update]
def index
#clients = Client.paginate(page: params[:page])
end
def new
#client = Client.new
end
def create
#client = Client.new(client_params)
if #client.save
flash[:success] = "Client added successfully"
redirect_to clients_path
else
render 'new'
end
end
def edit
end
def update
if #client.update(only_client_params)
flash[:success] = "Job updated successfully"
redirect_to clients_path
else
render 'edit'
end
end
private
def set_client
#client = Client.find(params[:id])
end
def client_params
params.require(:client).permit(
:name,
:phone,
:fax,
:url,
:address,
:city,
locations_attributes: [
:site,
:fax,
:phone,
:url,
:address,
:_destroy
]
)
end
end
This is the client's edit.html.erb form
<%= form_for(#client, class: 'form-horizontal') do |f| %>
<%= render(partial: 'client_field', locals: {f: f}) %>
<%= f.fields_for :locations do |l| %>
<%= l.hidden_field :client_id, value: #client.id %>
<%= l.hidden_field :_destroy %>
<%= l.text_field :site, class: 'form-control' %>
<% end %>
<%= f.submit "Save", class: "btn lg-button" %>
<% end %>
Actually, you have two issues here. One in your Model and other in your Controller permitted params. Let's dig into them:
1) Model
The update_only option is ignored when used with collection association (it is your case), as said in Rails documentation:
For a one-to-one association, this option allows you to specify how
nested attributes are to be used when an associated record already
exists. In general, an existing record may either be updated with the
new set of attribute values or be replaced by a wholly new record
containing those values.
By default the :update_only option is false and the nested attributes are used to update the existing record only if they include
the record's :id value. Otherwise a new record will be instantiated
and used to replace the existing one.
However if the :update_only option is true, the nested attributes are
used to update the record's attributes always, regardless of whether
the :id is present. The option is ignored for collection
associations.
So, the first step would be removing the update_only option of your Client class, because it will be ignored since you have a has_many association (collection association) with locations:
class Client < ActiveRecord::Base
has_many :locations, :dependent => :destroy
accepts_nested_attributes_for :locations, :allow_destroy => true
end
2) Controller
You have to permit the :id key for the :locations_attributes array. Since your update_only option in the model was ignored, Rails needs the param to tell that it is a record that exists and it's being updated rather than created.
You can solve your issue using the following in your client_params method (note the addition of the id key in your :locations_attributes key):
def client_params
params.require(:client).permit(
:name,
:phone,
:fax,
:url,
:address,
:city,
locations_attributes: [
:id, # Should be present; otherwise, Rails thinks that is a new record
:site,
:fax,
:phone,
:url,
:address,
:_destroy
]
)
end
I hope it helps !!
I have two associated models in my Rails 4 app: product.rb and image.rb. The Image model allows attached files using the Paperclip gem.
Images belong_to a Product, and a product has_many Images.
I would like to use the Product's new view to create and attach an image when I create a product. Each time I try, the parameters associated with the Paperclip image do not get saved, and end up nil.
Here are the models:
Product.rb
class Product < ActiveRecord::Base
validates :name, :part_number, presence: true
has_many :images, dependent: :destroy
belongs_to :category
accepts_nested_attributes_for :images, allow_destroy: true
end
Image.rb
class Image < ActiveRecord::Base
belongs_to :product
has_attached_file :image
end
Looking through past Stack Overflow questions I've found some relevant questions and answers, but none that seem to help my particular situation. So far I'm able to submit the file with the correct attributes, but it doesn't save them to the database.
ProductsController#create
def create
#product = Product.new(product_params)
end
def product_params
params.require(:product).permit(:name,
:category_id,
:part_number,
images_attributes: [:image])
end
ProductsController#new
def new
#product = Product.new
#categories = # irrelevant to question
end
products/new.html.haml
= form_for #product, :html =>
= f.label :name
= f.text_field :name
= f.label :part_number
= f.text_field :part_number
= f.label :category_id
= f.select :category_id, #ca
= f.fields_for :image do |ff|
= ff.label :image
= ff.file_field :image
= f.submit('Create Product')
Looking at the parameters I can tell that the correct attributes are being passed on:
Example parameters:
{"utf8"=>"✓",
"authenticity_token"=>"jGNy/TasHzP0EcWDFzZ3XH5/fpxb6vD+ncQ2PZSQ3/I=",
"product"=>{"name"=>"bar",
"part_number"=>"foo",
"category_id"=>"30",
"image"=>{
"image"=>#<ActionDispatch::Http::UploadedFile:0x007fc82f58e0e0
#tempfile=#<Tempfile:/var/folders/04/23d9grkj5fv3wkj2t8858kx40000gn/T/RackMultipart20131029-64949-1ldkz4g>,
#original_filename="FPE230_b.jpg",
#content_type="image/jpeg",
#headers="Content-Disposition: form-data; name=\"product[image][image]\"; filename=\"FPE230_b.jpg\"\r\nContent-Type: image/jpeg\r\n">}},
"commit"=>"Create Product"}
However, the image is not actually being saved to the database. How can I rectify this?
EDIT
Looking more closely at the logs, I see that I'm getting an "unpermitted parameters: image" error. This is even after adding images_attributes: [:image] to my product_params method.
I was able to get a setup similar to yours working with the following changes:
In ProductsController#create, instead of building the image separately, let the nested attributes handle it and just do:
#product = Product.new(product_params)
And allow the nested parameters (I figured out the image_attributes: [:image] from this question):
def product_params
params.require(:product).permit(:name, :part_number, images_attributes: [:image])
end
This is what my parameters look like in the logs for a create request:
{
"utf8"=>"✓",
"authenticity_token"=>"7EsPTNXu127itgp4fbohu672/L4XnSwLEkrqUGec3pY=",
"product"=>{
"name"=>"whatever",
"part_number"=>"12345",
"images_attributes"=>{
"0"=>{
"image"=>#<ActionDispatch::Http::UploadedFile:0x007feb0c183b08
#tempfile=#<Tempfile:/var/folders/6k/16k80dds0ddd6mhvs5zryh3r0000gn/T/RackMultipart20131031-51205-emaijs>,
#original_filename="some-image.png",
#content_type="image/png",
#headers="Content-Disposition: form-data; name=\"product[images_attributes][0][image]\"; filename=\"ponysay.png\"\r\nContent-Type: image/png\r\n">
}
}
},
"commit"=>"Create"}
And I see an insert into products and an insert into images.
If that doesn't work, could you post your ProductsController#new and your new product form?
EDIT AFTER YOUR EDIT:
I have 3 different things in my app/views/products/new.html.erb and ProductsController#new that might be affecting your results:
In the form_for, I have multipart: true as in the paperclip documentation:
<%= form_for #product, :url => products_path, :html => { :multipart => true } do |f| %>
Because Product has_many :images, in my form I have fields_for :images instead of fields_for :image:
<%= f.fields_for :images do |i| %>
<%= i.file_field :image %>
<% end %>
This fields_for doesn't show anything on the page unless in the ProductsController#new I build an image; does your Product.new do this by any chance?
def new
#product = Product.new
#product.images.build
end
Are there any particular reasons for these differences you have? Does your code work if you change it to match mine?