LoginSignup
4
4

More than 5 years have passed since last update.

【Rails】画像をドラッグ&ドロップでアップロード時に親モデルのIDを紐づける方法

Last updated at Posted at 2017-02-17

ドラッグ&ドロップでのファイルアップロードに苦戦しています。

ファイルのアップロードはgem'Dropzonejs'を使っています。

店舗を検索するサイトを作っており、住所等の店舗情報を入力するフォーム内に、同時に画像データも保存させるようにしたいと考えております。

Dropzonejsはデフォルトではデータをドラッグしたタイミングに保存されるようになっています。
保存するタイミングをドラッグした時から保存ボタンを押した時に変更することは出来ました。

ただ、
①form_forでDropzonejsを使う方法
②shop_idをimageモデルに紐づける方法
が解決出来ずにおります。

①はDropzonejsではデフォルトでform_tagとして使えるようになっています。
Railsではform_forの方が使い勝手が良いので、変更したいと考えています。

②のshop_idが生成されてから保存させるには、after_save :@shop.image.save等コールバックが必要なのかとも感じています。
ですが、考えてみると画像モデルに親モデルを紐づけることは、一般的に普及している手法とも思えます。
(Facebookの画像を投稿する際、投稿モデルのIDに紐づけるなど)
なのでもっと簡単な方法が存在するのではとも考えていますが、ググっても解決出来ずに行き詰まっています。

現状はshopをcreate後、生成されたshop_idを用いて@shop.images.buildしていますが、店舗情報登録画面と画像登録画面が別になっておりスマートな方法ではありません。
何かヒントでもご教示頂けますと幸いです。

環境

Ruby2.3.3
Rails5.0.1

Gem

gem 'dropzonejs-rails'

モデル

shopモデル→imageモデルをネストしています。

shop(店舗)モデル

id name address
1 店舗名 住所

image(画像)モデル

id shop_id file
1 1 file

該当箇所のコード

routes.rb
resources :shops, only: [:show] do
    resources :images, only: [:new, :create, :destroy] do
      collection do
         post :upload
      end
    end
  end
shop.rb
class Shop < ApplicationRecord
  has_many :images, dependent: :destroy
end
image.rb
class ShopImage < ApplicationRecord
  belongs_to :shop, optional: true
  mount_uploader :file, ImageUploader
end
shops_controller.rb
  def new
    @shop = Shop.new
    @shop.images.build(file: params['file'])
  end

  def create
      @shop = current_user.shops.build(shop_params)
      respond_to do |format|
        if @shop.save
          format.html { redirect_to @shop, notice: '登録しました' }
          #format.html { redirect_to "/shops/#{@shop.id}/images/new", notice: '画像を登録してください。' }
          format.json { render :show, status: :created, location: @shop }
        else
          format.html { render :new }
          format.json { render json: @shop.errors, status: :unprocessable_entity }
        end
      end
  end
image_controller.rb
  def upload
    @shop = Shop.find(params[:shop_id])
    image = @shop.images.build(file: params['file'])
    image.save!
    render status: 200, json: @shop.images
 end
image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
  include Cloudinary::CarrierWave

  process :convert => 'jpg'
  process :tags => ['file']

  version :standard do
    process :resize_to_fill => [100, 150, :north]
  end

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

  version :_100x100 do
    process :resize_and_pad => [100, 100, '#fff']
  end

  version :_280x280 do
    process :resize_and_pad => [280, 280, '#fff']
  end

  version :_540x540 do
    process :resize_and_pad => [540, 540, '#fff']
  end

  version :_800x800 do
    process :resize_and_pad => [800, 800, '#fff']
  end

  def public_id
    return model.id
  end

  def extension_white_list
     %w(jpg jpeg gif png)
  end

  def filename
    super.chomp(File.extname(super)) + '.jpg' if original_filename.present?
  end


  def store_dir
    "mypath"
  end
shops/new.html.erb
<%= form_for (@shop) do |f| %>
    <div class="fallback">
        <%#= form_tag(upload_shop_images_path(@shop),
            :id => 'my-dropzone', :class => 'dropzone', method: :post) %>
        <!--※ ↓無理やりform_for形式にしてみましたが、やはり動きませんでした。-->
        <%= f.file_field :image, :id => 'my-dropzone', :class => 'dropzone', method: :post %>
    </div>

    <div class="form-group">
        <%= f.label :name, :class => "col-sm-3 control-label" %>
        <div class="col-sm-7">
            <%= f.text_field :name, :class => "form-control" %>
        </div>
    </div>

    <div class="form-group">
        <%= f.label :address, :class => "col-sm-3 control-label" %>
        <div class="col-sm-7">
            <%= f.text_field :address, :class => "form-control" %>
        </div>
    </div>
    <%= f.submit "登録", :class => 'btn btn-primary', data: { disable_with: '登録中...' }  %>
<% end %>

<script type="text/javascript">
  Dropzone.options.myDropzone = {
    autoProcessQueue: false,

  init: function() {
    var submitButton = document.querySelector("#submit-all")
        myDropzone = this;

    submitButton.addEventListener("click", function() {
      myDropzone.processQueue(); 
    });

    this.on("addedfile", function() {
    });
  }
};
</script>

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4