LoginSignup
0
1

More than 3 years have passed since last update.

Rails ファイル添付機能 複数モデルでの共通化

Last updated at Posted at 2019-10-12

はじめに

DRYの考え方を意識しはじめ、効率的なコーディングだと感じたので備忘録としてまとめます。

構成

UserモデルとPostモデルの両方にファイル添付機能を持たせたいとき、
Attachment_fileモデルに添付ファイルを格納して、
中間テーブルとしてUser_attachment_fileモデルとPost_attachment_fileモデルを設ける。

  • Attachment_fileモデル
  • Userモデル
  • User_attachment_fileモデル
  • Postモデル
  • Post_attachment_fileモデル

Gemfile

下記を書いて、bundle install

gem 'carrierwave'

コーディング

 Model

  • Attachment_fileモデル
  • Userモデル
  • User_attachment_fileモデル
  • Postモデル
  • Post_attachment_fileモデル
attachment_file.rb
mount_uploader :file, FileUploader
after_destroy :destory_intermediate

has_many :user_attachment_files
has_many :users, through: :user_attachment_files

has_many :post_attachment_files
has_many :posts, through: :post_attachment_files

def destory_intermediate
  user_attachment_files.destroy if user_attachment_files.present?
  post_attachment_files.destroy if post_attachment_files.present?
end
user.rb
class User < ActiveRecord::Base
  has_many :user_attachment_files, dependent: :destroy
  has_many :attachment_files, through: :user_attachment_files, dependent: :destroy
end
user_attachment_file.rb
class UserAttachmentFile < ActiveRecord::Base
  belongs_to :user
  belongs_to :attachment_file
end
post.rb
class Post < ActiveRecord::Base
  has_many :post_attachment_files, dependent: :destroy
  has_many :attachment_files, through: :post_attachment_files, dependent: :destroy
end
post_attachment_file.rb
class PostAttachmentFile < ActiveRecord::Base
  belongs_to :post
  belongs_to :attachment_file
end

Controller

  • Attachment_fileモデル

  • Attachment_fileモデル

attachment_files_controller.rb
class AttachmentFilesController < ApplicationController
  def create
    @attachment_file = AttachmentFile.create!(file_params)

    respond_to do |format|
      format.html
      format.json {render json: @attachment_file }
    end
  end

  def destroy
    @attachment_file = AttachmentFile.find(params[:id])
    if @attachment_file.destroy
      render :json => { result: 'success' }
    else
      render :json => { result: 'error' }
    end
  end

  private

  def file_params
    params.require(:attachment_file).permit(:id, :file)
  end
end

View

Dropzoneを使っています。ここでは詳細は割愛します。

(Dropzone.jsで複数ファイルアップロード後、削除したいファイルを指定したい:https://qiita.com/saekis/items/207379a056af73f143b7)

users/new.html.slim
tr
  th = label :user, :user_file
  td
    #user_file_uploader.dropzone                  / ポイント
    table.files
      - for file in @user.attachment_files do
        tr id="file_row_#{file.id}"
          td
            = file.original_filename
            = hidden_field_tag nil, file.id, name:'user[attachment_file_ids][]'
          td
            = link_to file.file_url, download: file.original_filename do
              = fa_icon "download"
              |ダウンロード
          td
            = link_to '#', onclick: "#", file_id: file.id, remote: true do
              |削除

Table

中間テーブル(User_attachment_file、Post_attachment_file)を持っているため、UserモデルとPostモデルではカラムを設ける必要はありません。

  • Userモデル
  • User_attachment_fileモデル
  • Postモデル
  • Post_attachment_fileモデル
  • Attachment_fileモデル
schema.rb
# Userモデル
create_table "users", force: :cascade, options: do |t|
  t.datetime "created_at",  null: false
  t.datetime "updated_at",  null: false
end

# User_attachment_fileモデル
create_table "user_attachment_files", force: :cascade, options: do |t|
  t.integer  "user_id",            limit: 4, null: false          # ポイント
  t.integer  "attachment_file_id", limit: 4, null: false          # ポイント
end

# Postモデル
create_table "posts", force: :cascade, options: do |t|
  t.datetime "created_at",  null: false
  t.datetime "updated_at",  null: false
end

# Post_attachment_fileモデル
create_table "post_attachment_files", force: :cascade, options: do |t|
  t.integer  "post_id",            limit: 4, null: false          # ポイント
  t.integer  "attachment_file_id", limit: 4, null: false          # ポイント
end

# Attachment_fileモデル
create_table "attachment_files", force: :cascade, options: do |t|
  t.string   "file",              limit: 255                      # ポイント
  t.string   "original_filename", limit: 255                      # ポイント
  t.datetime "created_at",                    null: false
  t.datetime "updated_at",                    null: false
end

まとめ

先輩のコードを見てこの方法を知りました。
人のコードを読むことの重要性を強く感じたので、コーディング → リファクタリング のサイクルを大切にしたいです。

0
1
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
0
1