6

I am using Carrierwave, Fog to store images upload to S3.

The issue is that when I go to edit a record, the "file" & "photo" field automatically goes blank. So if I want to preserve the image or file in the record I have to re-upload it.

Otherwise, what happens is that the uploaded file/image just disappears. I am not even sure if it gets deleted from S3, but the association with my record in the db disappears.

This is my SimpleForm _form.html.erb partial for my Post model:

<%= simple_form_for(@post, html: {class: 'form-horizontal' }) do |f| %> 
    <%= f.error_notification %>
    <%= f.input_field :title, placeholder: "Enter Title"  %>
    <%= f.input_field :body, id: "body-field", placeholder: "Provide all the facts." %>
    <%= f.input_field :photo %>
    <%= f.input_field :file %>
    <%= f.button :submit, class: "btn btn-primary pull-left" %>
<% end %>

Here is my PhotoUploader

class PhotoUploader < CarrierWave::Uploader::Base
  include CarrierWave::RMagick
  storage :fog

  include CarrierWave::MimeTypes
  process :set_content_type

  def store_dir
    "images/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

    version :main_thumb_mobile do
      process :resize_to_fit => [52, 52]
    end

    version :main_thumb do
      process :resize_to_fit => [150, 150]
    end

    version :post_thumb do
      process :resize_to_fit => [200, 200]
    end

    version :large do
      process :resize_to_limit => [400, 400]
    end

  def extension_white_list
    %w(jpg jpeg gif png)
  end
end

This is my FileUploader

class FileUploader < CarrierWave::Uploader::Base
  storage :fog
  def store_dir
    "files/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

  def extension_white_list
    %w(pdf doc docx xls xlsx ppt pptx txt mp4 m4v mov avi mkv mp3 wav)
  end    
end

This is the Schema for my Post:

# == Schema Information
# truncated for brevity
# Table name: posts
#
#  id                    :integer          not null, primary key
#  title                 :text
#  photo                 :string(255)
#  body                  :text
#  user_id               :integer
#  file                  :string(255)

Edit 1

This is my Post.rb:

# == Schema Information
#
# Table name: posts
#
#  id                    :integer          not null, primary key
#  title                 :text
#  photo                 :string(255)
#  body                  :text
#  created_at            :datetime
#  updated_at            :datetime
#  user_id               :integer
#  ancestry              :string(255)
#  file                  :string(255)
#  status                :integer          default(0)
#  slug                  :string(255)
#  publication_status    :integer          default(0)
#  has_eyewitness        :boolean          default(FALSE)
#  youtube_embed_code    :text
#  soundcloud_embed_code :text
#

class Post < ActiveRecord::Base
  has_ancestry
  belongs_to :user
  resourcify
  enum status: [ :unconfirmed, :corroborated, :confirmed ]
  enum publication_status: [ :unpublished, :published ]
  extend FriendlyId
  friendly_id :title, use: [:slugged, :history, :finders]

  attr_accessor :country

  mount_uploader :photo, PhotoUploader
  mount_uploader :file, FileUploader

  validates_presence_of :body
  validates_length_of :body, maximum: 150, too_long: 'The report must be less than 150 words.',
                        tokenizer: ->(str) { str.scan(/\w+/) }
  validates_length_of :title, maximum: 7, too_long: 'The title must be less than 7 words.',
                                              tokenizer: ->(str) { str.scan(/\w+/) }

  def publish
    published!
  end

  def unpublish
    unpublished!
  end

  def is_published?
    if self.published?
      "yes"
    else
      "no"
    end
  end      
end

Edit 2

I changed the f.input_field :photo in my form to f.file_field :photo per jaspreet's suggestion and it still doesn't work. This is my log from an update request:

Started PATCH "/posts/ognr-takes-over-amcham-6a6f01ba-a9f9-44d5-924a-72f666f20ca8" for 127.0.0.1 at 2014-12-28 13:51:38 -0500
Processing by PostsController#update as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"S7FOq956fox6XYpar7Yd6V7AL+bwypQQxZktjHW3PNc=", "post"=>{"parent_id"=>"", "status"=>"unconfirmed", "publication_status"=>"unpublished", "title"=>"OGNR takes over AMCHAM", "body"=>"OGNR storms the rerl.", "has_eyewitness"=>"1", "youtube_embed_code"=>"", "soundcloud_embed_code"=>""}, "commit"=>"Update Post", "id"=>"ognr-takes-over-amcham-6a6f01ba-a9f9-44d5-924a-72f666f20ca8"}
  User Load (1.7ms)  SELECT  "users".* FROM "users"  WHERE "users"."id" = 1  ORDER BY "users"."id" ASC LIMIT 1
   (1.2ms)  SELECT COUNT(*) FROM "roles" INNER JOIN "users_roles" ON "roles"."id" = "users_roles"."role_id" WHERE "users_roles"."user_id" = $1 AND (((roles.name = 'admin') AND (roles.resource_type IS NULL) AND (roles.resource_id IS NULL)))  [["user_id", 1]]
   (6.4ms)  SELECT COUNT(*) FROM "roles" INNER JOIN "users_roles" ON "roles"."id" = "users_roles"."role_id" WHERE "users_roles"."user_id" = $1 AND (((roles.name = 'editor') AND (roles.resource_type IS NULL) AND (roles.resource_id IS NULL)))  [["user_id", 1]]
   (0.2ms)  BEGIN
  Post Exists (1.0ms)  SELECT  1 AS one FROM "posts" INNER JOIN "friendly_id_slugs" ON "friendly_id_slugs"."sluggable_id" = "posts"."id" AND "friendly_id_slugs"."sluggable_type" = 'Post' WHERE ("posts"."id" IS NOT NULL) AND "posts"."slug" = 'ognr-takes-over-amcham' LIMIT 1
  SQL (0.6ms)  INSERT INTO "posts" ("body", "created_at", "has_eyewitness", "slug", "soundcloud_embed_code", "title", "updated_at", "youtube_embed_code") VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING "id"  [["body", "OGNR storms the rerl."], ["created_at", "2014-12-28 18:51:39.183091"], ["has_eyewitness", "t"], ["slug", "ognr-takes-over-amcham-cd491a8e-9a4e-4d5e-b13a-898de4adf135"], ["soundcloud_embed_code", ""], ["title", "OGNR takes over AMCHAM"], ["updated_at", "2014-12-28 18:51:39.183091"], ["youtube_embed_code", ""]]
  FriendlyId::Slug Load (0.5ms)  SELECT  "friendly_id_slugs".* FROM "friendly_id_slugs"  WHERE "friendly_id_slugs"."sluggable_id" = $1 AND "friendly_id_slugs"."sluggable_type" = $2  ORDER BY "friendly_id_slugs".id DESC LIMIT 1  [["sluggable_id", 43], ["sluggable_type", "Post"]]
  SQL (0.4ms)  DELETE FROM "friendly_id_slugs" WHERE "friendly_id_slugs"."sluggable_id" = $1 AND "friendly_id_slugs"."sluggable_type" = $2 AND "friendly_id_slugs"."slug" = 'ognr-takes-over-amcham-cd491a8e-9a4e-4d5e-b13a-898de4adf135'  [["sluggable_id", 43], ["sluggable_type", "Post"]]
  SQL (0.4ms)  INSERT INTO "friendly_id_slugs" ("created_at", "slug", "sluggable_id", "sluggable_type") VALUES ($1, $2, $3, $4) RETURNING "id"  [["created_at", "2014-12-28 18:51:39.242475"], ["slug", "ognr-takes-over-amcham-cd491a8e-9a4e-4d5e-b13a-898de4adf135"], ["sluggable_id", 43], ["sluggable_type", "Post"]]
   (2.6ms)  COMMIT
Redirected to http://localhost:3000/posts/ognr-takes-over-amcham-cd491a8e-9a4e-4d5e-b13a-898de4adf135
Completed 302 Found in 372ms (ActiveRecord: 17.3ms)

Edit 3

The PostController#Update looks like this:

class PostsController < ApplicationController
  load_and_authorize_resource

  def update
    respond_to do |format|
      if @post.update(post_params)
        format.html { redirect_to @post, notice: 'Report was successfully updated.' }
        format.json { render :show, status: :ok, location: @post }
      else
        format.html { render :edit }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

Edit 4

Routes

marcamillion
  • 32,933
  • 55
  • 189
  • 380
  • Can you add the code of your `Post` model? – fivedigit Dec 21 '14 at 09:50
  • After seeing the logs, my thoughts have completely changed. How is the rails firing a INSERT query when you have asked for a PATCH. It should fire a UPDATE query. I request to share your controllers update action code too. – jaspreet21anand Dec 28 '14 at 22:57
  • Now check where the routes are pointing to. Controller action looks good. Just run 'rake routes' and send screen shot or text. – jaspreet21anand Dec 28 '14 at 23:08
  • @jaspreet21anand Done. Refresh question. – marcamillion Dec 28 '14 at 23:22
  • Hey @marcamillion, the problem is with the query fired. I double checked my apps, it fires UPDATE query in mysql when you update any record. Where as in your case, it is creating new record!! :O INSERT query is being fired. Please try to see to it first. 1. check if duplicate records are being created. 2. if number 1 is happening, then check why is it happening. – jaspreet21anand Dec 29 '14 at 07:07

4 Answers4

4

You need to use an input field for upload image to show the image name, and use javascript function to display the file name in input field as the following:

<%= text_field_tag 'file_name', "#{@post.photo_file_name}", {disabled: true, class: 'browse-input form-control'} %>
<%= f.input_field :photo %>
:javascript
  $("#post_photo").change(function () {
      $("#file_name").val(this.value.replace(/^.*[\\\/]/, ''));
  });

And You can use the same code for upload file.

For Prevent to re-upload unchanged photo

You can use changed? method to check if photo changed or not as the following:

def update
  if @post.photo && !@post.photo.changed?
     @post.update(post_update_params_without_photo)
  end
end
# Example method, don't forgot set the necessary attributes
def post_update_params_without_photo
  params.require(:post).permit(:title)
end

Explain: This code to update changed attributes only i.e (title, status, and so on).

Mohamed Yakout
  • 2,868
  • 1
  • 25
  • 45
  • I am a little confused by that first line...is it a comment or is it supposed to be code? Should that be `f.input`? Is that standard ERB? Doesn't seem so to me. – marcamillion Dec 22 '14 at 17:16
  • Sorry, I just want to add input field, but I forgot syntax of erb, because of using haml :) :) – Mohamed Yakout Dec 22 '14 at 19:44
  • 1
    I updated my answer using [text_field_tag](http://apidock.com/rails/ActionView/Helpers/FormTagHelper/text_field_tag) – Mohamed Yakout Dec 22 '14 at 19:45
  • But will this prevent my form from re-uploading a blank entry for the `f.input_field :photo`? It seems that this just shows me the name of the file, but it doesn't re-attach the file that is already in the db for the existing record. In other words, when a user has already uploaded a file and they choose to edit the record, I want the form to show the file (in the `f.input_field`) - so that when the record is updated (i.e. the edit form is submitted), the file/photo does not need to be re-uploaded. – marcamillion Dec 22 '14 at 21:08
  • Yeah!, This will update the text_field, but when you submit with new file, and go to edit page again, but if you want to update the text_file automatically, I think you need to make javascript event on open button for browser window, and I don't try that before, but I'll search to get the solution, because I need that also :) – Mohamed Yakout Dec 22 '14 at 21:19
  • I think you misunderstood me. I don't want to update the `text_field` if I don't have to. i.e. if there has been a file/photo previously uploaded, I don't want to have to re-upload that file. I want to keep it, unless the user explicitly chooses a new file. – marcamillion Dec 22 '14 at 22:14
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/67521/discussion-between-mohamed-yakout-and-marcamillion). – Mohamed Yakout Dec 23 '14 at 09:00
  • That looks much better. But thinking about this some more - and perhaps I should test first, but just thinking through it....what this does is checks to see if the photo being sent in the form has been changed, right? If so, then it updates it accordingly. If not, then it doesn't. The issue is that if the form doesn't have the photo/file attached to the form, then it will substitute it with an empty value (aka it will be `updated`), right? i.e. if the form detects no photo attached, then the `@post.photo.changed?` will return `true`. So it ALMOST gets us there, but not quite. – marcamillion Dec 23 '14 at 19:16
  • I update my code by change condition as `if @post.photo && !@post.photo.changed?`, you can check on photo, if it was null don't update photo in permit attributes. – Mohamed Yakout Dec 23 '14 at 22:50
4

I think the simple_form_for is doing some extra work in making params. It might be sending params as 'post' => { 'file' => { 'id' => '29', 'image' => '' } }, when no file is selected.

Whereas if you try to use a simple file_field_tag or f.file_field then it only sends as 'post' => { 'file' => { 'id' => '29' } }, if no image file is selected, and the old one is preserved.

Try this and it would surely solve your problem.

Remember if using file_field_tag you need to pass the name attribute correctly for the params to be built up in the right way as the rails expect. In your case it must be like 'post[file][image]' and a hidden field too(this is automatically built if using f.file_field) whose name will be post[file][id]

Sharing my console

Started PATCH "/requirements/2162" for 127.0.0.1 at 2015-01-05 10:44:53 +0530
Processing by RequirementsController#update as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"mqoPYjGir/3aA/ZFCjpr79KY/xXEoyTd95Dis8HDH7g=", "requirement"=>{"category_ids"=>["40", "47"], "title"=>"gefd gefd gefd", "details"=>"dsfg", "expiration_date(1i)"=>"2015", "expiration_date(2i)"=>"12", "expiration_date(3i)"=>"31", "address_attributes"=>{"street"=>"ewfg", "city"=>"deaf", "country_code"=>"DZ", "state_code"=>"01", "id"=>"60"}}, "commit"=>"Update Requirement", "id"=>"2162"}
  User Load (4.3ms)  SELECT  `people`.* FROM `people`  WHERE `people`.`type` IN ('User') AND `people`.`id` = 28  ORDER BY `people`.`id` ASC LIMIT 1
  Requirement Load (1.0ms)  SELECT  `requirements`.* FROM `requirements`  WHERE `requirements`.`id` = 2162 LIMIT 1
   (1.7ms)  BEGIN
  Category Load (6.1ms)  SELECT `categories`.* FROM `categories`  WHERE `categories`.`id` IN (40, 47)
  Category Load (5.0ms)  SELECT `categories`.* FROM `categories` INNER JOIN `category_requirements` ON `categories`.`id` = `category_requirements`.`category_id` WHERE `category_requirements`.`requirement_id` = 2162
  Address Load (0.4ms)  SELECT  `addresses`.* FROM `addresses`  WHERE `addresses`.`id` = 60 LIMIT 1
  DonorRequirement Exists (0.4ms)  SELECT  1 AS one FROM `donor_requirements`  WHERE `donor_requirements`.`requirement_id` = 2162 LIMIT 1
  SQL (7.6ms)  UPDATE `requirements` SET `expiration_date` = '2015-12-31', `title` = 'gefd gefd gefd', `updated_at` = '2015-01-05 05:14:53' WHERE `requirements`.`id` = 2162
   (6.0ms)  COMMIT
jaspreet21anand
  • 779
  • 1
  • 6
  • 8
  • So just to be clear....do you mean the `f.input_field :file` is the one sending those params. Or do you mean the `simple_form_for` form handler? I am trying to figure out if you mean I should change the entire `simple_form` handler? – marcamillion Dec 28 '14 at 06:01
  • I am not sure which of it is doing that. I ll have to try that out. But definitely don't change the whole thing. Just try replacing f.input_field with file_field_tag and see if works. If it does, then try to find who is damaging, simple form or f.input_field.. ;) – jaspreet21anand Dec 28 '14 at 12:58
  • Yeh the `f.input_field` didn't work. I updated the question with a log from an update request. Let me know your thoughts. I don't want to change the entire `simple_form_for` if I don't have to. It is going to be a pain to get everything working in a normal form. – marcamillion Dec 28 '14 at 18:55
  • After seeing the logs, my thoughts have completely changed. How is the rails firing a INSERT query when you have asked for a PATCH. It should fire a UPDATE query. I request to share your controllers update action code too. – jaspreet21anand Dec 28 '14 at 22:28
  • Check question again. – marcamillion Dec 28 '14 at 23:25
  • Hey marc, I couldn't find any problem with the code but I am surprised to see the INSERT query being fired. If you see my console, UPDATE query is fired when I edit any record. – jaspreet21anand Jan 05 '15 at 05:28
0

The simplest solution I found is to simply add a check to see if there is a photo attached, like if @post.photo? and then just display a label if that is the case.

This, ofcourse assumes that I don't want to change the image.

<% if @post.photo? %>
    <label><%= @post.photo.file.filename %></label>
<% else %>
    <label for="photoFileUpload">Upload Images:</label>
    <%= f.input_field :photo %>
    <p class="help-block">Allowed: png, gif, jpg/jpeg</p>
<% end %>
marcamillion
  • 32,933
  • 55
  • 189
  • 380
0

In your controller do: It works for me.

  def update
   @article = Article.find(params[:id])
    article = article_params
    #no image(s) attached do not update the images uploaded
    if (article_params[:images].count - 1 ) == 0
      article ={
        id: @article.id,
        name: article_params[:title]
      } 
    end

      respond_to do |format|
        if @article.update(article)
          format.html { redirect_to property_url(@particle), notice: "Property was successfully updated." }
          format.json { render :show, status: :ok, location: @article }
        else
          format.html { render :edit, status: :unprocessable_entity }
          format.json { render json: @article.errors, status: :unprocessable_entity }
        end
      end
   
  end