I've been researching this issue for a couple days and can't seem to find a solution.
I have a model in Rails 6 for bars that has_one_attached :image
A user can create a new bar and attach an image. This works fine.
When a user edits bar information like a bar's name and submits that information Active Storage purges the originally uploaded image by default, even if no new image was submitted.
During an edit, if a user does not submit a new image, and ONLY changes the record, e.g., Bar Name, how do I keep Active Storage from automatically purging the image? I would like to keep the image associated with the Bar record.
I have tried dependent: :purge_later
in my model to no success. I have also tried submitting params without the image param if bar_info.image.attached?
My Model bar_info.rb
class BarInfo < ApplicationRecord
belongs_to :user
has_many :bar_specials, dependent: :destroy
has_one_attached :image
validates :user_id, presence: true
validates :barname, presence: true, length: { maximum: 100 }
validates :city, presence: true, length: { maximum: 50 }
validates :state, presence: true, length: { maximum: 50 }
validates :image, content_type: { in: %w[image/jpeg image/gif image/png],
message: "must be a valid image format" },
size: { less_than: 5.megabytes,
message: "should be less than 5MB" }
end
My Controller bar_infos_controller.rb
class BarInfosController < ApplicationController
def update
@bar_info = BarInfo.find(params[:id])
@bar_info.user_id = current_user.id
@bar_info.image.attach(params[:bar_info][:image])
if @bar_info.update(bar_info_params)
flash[:notice] = "Bar Info Updated!"
redirect_to bar_info_path(@bar_info)
else
render "edit"
end
end
private
def bar_info_params
attributes = [ :barname, :city, :state, :image ]
params.require(:bar_info).permit(attributes)
end
end
My Edit View _form.html.erb
<div class="row">
<div class="col-md 6">
<%= form_with(model: @bar_info, local: true) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label "Bar Name (required)" %>
<%= f.text_area :barname, placeholder: "Bar name here", class: "form-control", size: "70x1" %>
<%= f.label "City (required)" %>
<%= f.text_area :city, placeholder: "San Diego", class: "form-control", size: "70x1" %>
<%= f.label "State (required)" %>
<%= f.select :state, CS.states(:us), class: "form-control" %>
<span class="image">
<p>Upload an image of Bar here!</p>
<%= f.file_field :image, accept: "image/jpeg,image/gif,image/png" %>
</span>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
<script type="text/javascript">
$("#bar_info_image").bind("change", function() {
const size_in_megabytes = this.files[0].size/1024/1024;
if (size_in_megabytes > 5) {
alert("Maximum file size is 5MB. Please choose a smaller file.");
$("$bar_info_image").val("");
}
});
</script>
</div>
</div>
bar_infos routes:
bar_infos GET /bar_infos(.:format) bar_infos#index
POST /bar_infos(.:format) bar_infos#create
new_bar_info GET /bar_infos/new(.:format) bar_infos#new
edit_bar_info GET /bar_infos/:id/edit(.:format) bar_infos#edit
bar_info GET /bar_infos/:id(.:format) bar_infos#show
PATCH /bar_infos/:id(.:format) bar_infos#update
PUT /bar_infos/:id(.:format) bar_infos#update
DELETE /bar_infos/:id(.:format) bar_infos#destroy
TL;DR I'd like to edit a bar's name and still keep the original image that was uploaded with it. Active Storage currently purges that image on update.
Thank you.
It is because of this line in your update
controller action:
def update
...
@bar_info.image.attach(params[:bar_info][:image])
...
end
if user doesn't fill in the image in edit action you will set image to nil
which deletes the file from activestorage. You should leave this update completely on ActiveRecords update
action. You have image in your permitted params so it should work without any problems:
def update
@bar_info = BarInfo.find(params[:id])
@bar_info.user_id = current_user.id
if @bar_info.update(bar_info_params)
flash[:notice] = "Bar Info Updated!"
redirect_to bar_info_path(@bar_info)
else
render "edit"
end
end
private
def bar_info_params
attributes = [ :barname, :city, :state, :image ]
params.require(:bar_info).permit(*attributes)
end
(also I think there should be *
in permit call params.require(:bar_info).permit(*attributes)
)