How do I duplicate an ActiveRecord object with a dragonfly image?
I have the following.
model:
class Event < ActiveRecord::Base
image_accessor :thumbnail
attr_accessible :thumbnail, :remove_thumbnail, :retained_thumbnail
validates :thumbnail, presence: true
end
controller:
def clone
#event = Event.find(1).dup
render :new
end
view:
<%= form_for #event do |f| %>
<%= f.label :thumbnail %>
<%= image_tag(#event.thumbnail.thumb('100x75').url) %>
<label><%= f.check_box :remove_thumbnail %> Remove?</label>
<%= f.file_field :thumbnail %>
<%= f.hidden_field :retained_thumbnail %>
<% end %>
When I render the form, the image displays, but on submit, the image gets cleared out.
One thing, I'd like to make sure they are actually different images, so if I edit the original record, it will not affect the duplicate.
Here's how I got it to work, overriding the object's dup behavior:
def dup
target = Event.new(self.attributes.reject{|k,v| ["id", "attachment_uid"].include?(k) })
target.attachment = self.attachment
target
end
Then, when you call save on the target the image will be copied to the new location.
Note that on the first line I first tried target = super, to utilize the object's default dup behavior, but that caused the files of the original object to be deleted. The above solution finally did the trick for me.
Related
I have a form for a blog, and I would like to have two field for images. One image being the cover (in Show) and another image will serve as a preview (in index).
My form looks as follow:
<%= semantic_form_for #blog, :html => { :multipart => true } do |f| %>
<%= t :Choose_File_for_cover %> <%= f.file_field :image_path, id: "avatar-upload2", required: true %>
<img id="img_prev3" width="100%" height=200 src="#" alt="your image" class="img-thumbnail hidden"/>
<%= t :Choose_File_for_homepage %> <%= f.file_field :homepagepic, id: "avatar-upload3", required: true %>
<%= f.hidden_field :image_path_cache %>
<%= f.hidden_field :homepagepic_cache %>
<%= f.actions do %>
<%= f.action :submit, :as => :input %>
<% end %>
<% end %>
My model looks like:
class Blog < ApplicationRecord
belongs_to :user
acts_as_taggable
mount_uploader :image_path, BlogUploader
mount_uploader :homepagepic, BlogcoverUploader
end
It works well when I only have the image_path (the cover), but when I add a new field for homepagepic, i get a ROLLBACK at validation.
Can someone help me on how to select files through two separate fields on the same form please.
Thank you
The code you've provided is very sparse and it would be helpful to see a little bit more (e.g. the controller and the uploader).
I can, however, hazard a guess: image_path is an existing helper method provided by Rails (see https://api.rubyonrails.org/classes/ActionView/Helpers/AssetUrlHelper.html#method-i-image_path). I have absolutely no idea what happens when you use this as a name for a form field. It could also be because you declare your submit button to be an input (I've only ever seen and used as: :button for f.action :submit).
So overall, I would pick the following approach:
rename your upload fields to cover_image and the other one to preview_image (that's what you've described in your posts as their respective purpose, so you should name them accordingly)
change the submit to a button and remove all the noise from your template and start with the bare minimum: the two upload fields and nothing else (see sample code below – note that I haven't tested it but it should work or be very close to working)
after that works, start adding back the noise (i.e. the translations, the cache fields etc.)
Test that it still works after every step. If you can write a Capybara test, do that – otherwise test it manually.
If you have questions, feel free to ask.
<%= semantic_form_for #blog, html: { multipart: true } do |f| %>
<%= f.file_field :cover_image %>
<%= f.file_field :preview_image %>
<%= f.actions do %>
<%= f.action :submit, as: :button %>
<% end %>
<% end %>
class Blog < ApplicationRecord
belongs_to :user
acts_as_taggable
mount_uploader :preview_image, BlogUploader
mount_uploader :cover_image, BlogcoverUploader
end
As the previous poster said it's hard to debug your code without all the pieces to the puzzle. A ROLLBACK is happening because one or more validations failed.
Any time you have a ROLLBACK you can add a ! to the create or update method being called on the object being rolled back and ActiveRecord will throw an error telling you why the ROLLBACK happened instead of failing gracefully.
Once you know why your object isn't persisting you can check the params of the controller action that form is submitting to. Perhaps you forgot to whitelist a param via strong params?
I'm working on a project that requires an ActiveStorage has_many_attached :photos situation on a Location model.
I have the code set up below, but when attempting to upload a form, I receive the following error:
ActiveSupport::MessageVerifier::InvalidSignature in
LocationsController#attach_photo
Is this the way to "add" a file to the set of attachments for a particular parent record (i.e: a Location record)?
Location Model
class Location < ApplicationRecord
...
has_many_attached :photos
...
end
Locations Controller
class LocationsController < ApplicationController
...
def attach_photo
#location = Location.find(params[:id])
#location.photos.attach(params[:photo])
redirect_to location_path(#location)
end
...
end
View
<%= form_tag attach_photo_location_path(#location) do %>
<%= label_tag :photo %>
<%= file_field_tag :photo %>
<%= submit_tag "Upload" %>
<% end %>
View
resources :locations do
member do
post :attach_photo
end
end
Make sure to add multipart: true in form_tag. It generates enctype="multipart/form-data".
form_tag by default not responsible for it, must have it (if attaching a file).
multipart/form-data No characters are encoded. This value is required
when you are using forms that have a file upload control
Form:
<%= form_tag attach_photo_location_path(#location), method: :put, multipart: true do %>
<%= label_tag :photo %>
<%= file_field_tag :photo %>
<%= submit_tag "Upload" %>
<% end %>
Also:
Change post to put method, We are updating not creating Idempotency
resources :locations do
member do
put :attach_photo
end
end
You need to assign the signature (in params[:signed_blob_id]) to the instance as the example from the docs illustrates.
So, like this:
#location.photos.attach(params[:signed_blob_id]) # Signed reference to blob from direct upload
I solved this issue using this
def user_params
params.permit(
:id, :name, :email, :username, :country, :avatar, :id_number, :license_number
).select {|x,v| v.present?}
end
Looks like the empty value is causing the issue "avatar"=>""
"id_number"=>"234545", "license_number"=>"234545", "avatar"=>""
My model
class User < ApplicationRecord
has_one_attached :avatar
Rails Active Storage + React Native + React Native debugger with Network Inspect Enabled may cause this error.
If you're using React Native debugger with Network Inspect Enabled, file uploading might not be working because of this known issue: Formdata sends [object object] in request.
Turn off Network Inspect when you're using React Native debugger. You can inspect network payload with Reactotron instead.
Paperclip's document says to set attribute to nil for deleting attachments (in my case - photos).
I want users to be able to edit their projects, and delete the photos they've attached. When they're ready to delete the photos, they'll click on the "Update" button, because they're editing the projects, not just removing photos. I'm using checkboxes because each project can contain multiple photos:
<%= simple_form_for #project, html: { multipart: true } do |f| %>
<% #project.project_images.each do |pic| %>
<%= f.simple_fields_for :project_images, pic do |image_fields| %>
<%= image_tag pic.photo.url(:thumb) %>
<%= image_fields.check_box :_destroy %>
<% end %>
<% end %>
<%= f.button :submit %>
<% end %>
This is what it looks like:
So, how do I use this to delete my photos within my update method in the controller?
Current:
def update
#project = current_user.projects.find(params[:id])
respond_to do |format|
if #project.update(project_params)
if params[:photos]
params[:photos].each { |image|
#project.project_images.create(photo: image)
}
end
end
end
end
You can see that I'm still creating new images into the database if user decides to do so.
In my log, I got this
"project_images_attributes"=>{
"0"=>{"_destroy"=>"0", "id"=>"25"},
"1"=>{"_destroy"=>"1", "id"=>"26"},
"2"=>{"_destroy"=>"1", "id"=>"27"}}
This shows that I did indeed checked off photo id's 26 and 27 to be deleted. So now, I just want to know how to pass this to my update method to be destroyed? I would have to loop through this, check the ids that have _destroy => 1 to it, and then add nil and then save?
And lastly, my project params (which includes :_destroy)
def project_params
params.require(:project).permit(
project_images_attributes: [:id, :project_id, :photo, :_destroy])
end
Just add:
accepts_nested_attributes_for :project_images, allow_destroy: true
in your project.rb and pass the parameters as it is, as it seems you are doing it the right way. The rest rails will handle to delete or create a new record. You don't need to loop on the params[:photos].
Hope this helps.
I wanted to try out Carrierwave_direct with my app so I decided to follow the Railscast episode. http://railscasts.com/episodes/383-uploading-to-amazon-s3
I am using Carrierwave for a variety of uploads including Images, Videos, and Songs. Right now though, I'm only testing the video to see how I like it. Right now, the video's are getting uploaded to my Amazon bucket, but the file (string) isn't getting saved to my database and not rendering on the page. Here is where I am right now...
Rails v 4.0.1
Ruby v 2.1.1
Video Uploader
class VideoUploader < CarrierWave::Uploader::Base
include CarrierWaveDirect::Uploader
# I haven't added any resizing or anything yet
end
Video Model (video.rb)
mount_uploader :video, VideoUploader # In the database, the column is video:string
...
...
Page to upload file, before being redirected to form
def home
#uploader = Video.new.video
#uploader.success_action_redirect = new_video_url
end
Videos controller
def new
#video = Video.new(key: params[:key])
end
def create
#video = current_user.videos.create(video_params)
...
end
private
def video_params
params.require(:video).permit(:type, :title, :description, :video)
end
home.html.erb (where the video upload is BEFORE it successfully redirects you to the rest of the form)
<%= direct_upload_form_for #uploader do |f| %>
<p><%= f.file_field :video %></p>
<p><%= f.submit "Upload Video" %></p>
<% end %>
videos/_form.html.erb (the rest of the video form)
<%= form_for(#video, html: { :class => "full-form form-vertical" }) do |f| %>
<fieldset>
<%= render "shared/error_messages", object: f.object %>
<%= f.hidden_field :key %>
<div class="form-group">
<%= f.label :title %>
<%= f.text_field :title, :class => "form-control" %>
</div>
<%= f.submit %>
</fieldset>
<% end %>
My amazon is configured properly, so I won't provide my carrierwave.rb initializer file.
So to reiterate, what's happening is on the first page, I will upload the video which will redirect me to the form. If I inspect the form, I can see that the :key has the proper value in it. But, if I put a presence validator on the model, the form won't submit because the video will be blank. And if there is no validation, the form will submit, amazon will upload it to my bucket, but (1) the video file name/string won't be stored in my db and (2) it won't render on the page, because it's empty.
Thanks for taking a look. Cheers.
#video_params needs to permit :key, in your video controller.
private
def video_params
params.require(:video).permit(:type, :title, :description, :video, :key)
end
You brought the :key all the way down, set it in #new, kept it with the hidden_field, and then lost it on the strong params.
The :key needs to get back to the VideoUploader object which is mounted under Video as :video, this is what allows your Video class to accept the :key attribute.
Video Model (video.rb)
mount_uploader :video, VideoUploader # In the database, the column is video:string
...
In my rails project I use Carrierwave to upload images to S3 via fog. So far I have the Create Read and Delete portions of the CRUD spectrum working.
My problem is the edit/update portion. Im use the same _form.html.erb to edit that I used for creating records. When I click the edit link the form loads all of my data into the form fields for editing with the exception of the image. The form field is blank as though there is no image associated with the record.
How do I update an image that is already saved to S3?
Models/listing.rb
class Listing < ActiveRecord::Base
attr_accessible :body, :price, :title, :image
mount_uploader :image, ListingUploader
end
Controllers/listings_controller.rb (edit/update portion)
def edit
#listing = Listing.find(params[:id])
end
def update
#listing = Listing.find(params[:id])
if #listing.update_attributes(params [:listing])
redirect_to :action => 'show', :id => #listing
else
render :action => 'edit'
end
end
_form.html.erb
<%= form_for #listing, :html => { :multipart => true } do |l| %>
<p>
<%= l.label :title %>
<%= l.text_field :title %>
</p>
<p>
<%= l.label :price %>
<%= l.text_field :price %>
</p>
<p>
<label>Upload a Picture</label>
<%= l.file_field :image %>
<%= l.hidden_field :image_cache %>
</p>
<div class="image-pre">
<%= image_tag(#listing.image_url(:thumb)) if #listing.image? %>
</div>
<p>
<%= l.label :body %>
<%= l.text_area :body, :class => "tinymce" %>
<%= tinymce %>
</p>
<%= l.submit %>
<% end %>
In your listings_controller.rb, try something like:
def edit
#listing = Listing.find(params[:id])
#listing.image.cache!
end
Haven't tested this out myself, but I think it might work, given that image_cache field is used to circumvent this problem normally.
I had upgraded my app to Rails 6 sometime ago, then started experiencing this issue.
tldr; you need a carrierwave gem version 2.1.0 or higher for Rails 5.0 and up.
I used bundle outdated --strict to see if carrierwave could be upgraded. Check Gemfile to see if the requested version is set correctly. Also check Gemfile.lock to see if other gems are holding it back; you'll need to upgrade them as well. Then I used bundle update --strict to perform the actual upgrade.