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