[rails][まとめ]Active Adminをカスタマイズしていい感じの管理画面をつくる
Ruby2.1.4, Rails4.1.7で確認。
Railsで管理画面をつくる際に Active Adminを使うことが多いのですが、毎回手順を調べなおしたりしていたので、ひと通りのカスタマイズ方法をまとめておきます。
結構長くなってしまいました。
今回はサンプルとして、質問と質問に対する回答データを管理画面上で追加、閲覧、編集、削除できるようにしてみます。
今回のカスタマイズでできるようになることは
carrierwaveで画像アップロード
日付入力の際にカレンダーから日付選択
Question(親)作成時に、併せてAnswer(子)のデータも登録
の3つです。
最終的に↑の感じの管理画面になります。
テーブル構造は以下のようにします。
↑ rails-erd を使ってER図を作ってみました。
(NOT NULLの表示が崩れてしまいました)
今回作ったサンプルはgithubに上げてあります。
hilotter/rails_question_sample
それではrailsアプリの作成から順に始めます。
1. application templateを元に作成
以前作成したRails Application Templateを使ってRailsアプリケーションのひな形を作ります。
rails new question_sample -m https://raw.githubusercontent.com/hilotter/rails-application-template/master/app_template.rb -d mysql
今回、carrierwave、activeadminをインストール対象(y)にしました。
2. model作成
2-1. migration作成
- Question
./bin/rails g model question
vi db/migrate/20150127095640_create_questions.rb
class CreateQuestions < ActiveRecord::Migration
def change
create_table :questions do |t|
t.string :title, null: false
t.string :image
t.datetime :publish_datetime
t.timestamps
end
add_index :questions, :publish_datetime
end
end
- Answer
./bin/rails g model answer
vi db/migrate/20150127095659_create_answers.rb
class CreateAnswers < ActiveRecord::Migration
def change
create_table :answers do |t|
t.integer :question_id, null: false
t.string :answer_text, null: false
t.integer :total_count, null: false, default: 0
t.timestamps
end
add_index :answers, :question_id
end
end
2-2. model編集
- app/models/question.rb
class Question < ActiveRecord::Base
has_many :answers, dependent: :destroy
validates :title, :presence => true
validates :publish_datetime, :presence => true
validates :image, :presence => true
end
- app/models/answer.rb
class Answer < ActiveRecord::Base
belongs_to :question
validates :answer_text, :presence => true
end
3. active admin初期設定
- install
./bin/rails generate active_admin:install
./bin/rake db:migrate
- CRUD管理画面作成
./bin/rails g active_admin:resource Question
./bin/rails g active_admin:resource Answer
- rails server起動
./bin/rails s
これで http://localhost:3000/admin にアクセスすると以下のようにQuestionおよびAnswerのCRUD操作が可能になります。
- コメント表示を無効化
デフォルトで表示されているCommentsは使わないので無効化しておきます。
initializerの設定を変更します。
# config/initializers/active_admin.rb
config.comments = false
rails serverを再起動するとコメント表示が消えます。
ついでに、日本語設定も追加しておきます。
githubに日本語用localeが公開されているのでこちらをお借りしました。
rails-i18n/ja.yml at master · svenfuchs/rails-i18n
ja.ymlをconfig/locales/以下に設置しておきます。
これで基本的な管理画面設定ができました。
4. 管理画面をカスタマイズ
ここからが本番です。
基本的な機能はできましたが、このままだと管理画面を使う人にとって非常に使いづらいものになってしまっています。
また、画像に関しては現状アップロードできない状態です。
carrierwaveで画像アップロード
日付入力の際にカレンダーから日付選択
Question(親)作成時に、併せてAnswer(子)のデータも登録
をそれぞれ対応していきます。
4-1. carrierwaveで画像アップロード
まずは、画像アップロードができるようにします。
- アップローダー作成
./bin/rails g uploader Image
- uploaderを編集
デフォルト設定を元にfilenameとextension_white_listメソッドを有効にしました
app/uploaders/image_uploader.rb
# encoding: utf-8
class ImageUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
def extension_white_list
%w(jpg jpeg gif png)
end
def filename
'image' + File.extname(original_filename) if original_filename
end
end
- modelにuploaderを追加
# app/models/question.rb
class Question < ActiveRecord::Base
has_many :answers, dependent: :destroy
mount_uploader :image, ImageUploader
#・・・
end
この状態で管理画面にアクセスすると画像アップロードフォームに切り替わっています。
ただ、このままだとStrongParameters設定がないためエラーになります。
ActiveAdminの場合はpermit_paramsを設定することで対応できます。
# app/admin/question.rb
ActiveAdmin.register Question do
permit_params :title, :image, :image_cache, :publish_datetime
end
permit_paramsを設定した後、再度アップロードを試すとデータ作成が可能になっています。
4-2. 日付入力の際にカレンダーから日付選択
続いて、現状Publish datetimeの入力が全てセレクトボックスになっているのをカレンダーから日付選択できるようにします。
ActiveAdminでカレンダーから日付選択をするためのgemとして just-datetime-picker が公開されているのでこちらを使います。
まずはGemfileに追加します。
gem "just-datetime-picker"
bundle install後、jsとcss設定を追加します。
- css assetsを編集
# app/assets/stylesheets/active_admin.css.scss
@import "just_datetime_picker/base";
- js assetsを編集
# app/assets/javascripts/active_admin.js.coffee
#= require just_datetime_picker/nested_form_workaround
- modelにpublish_datetime設定追加
# app/models/question.rb
class Question < ActiveRecord::Base
has_many :answers, dependent: :destroy
mount_uploader :image, ImageUploader
just_define_datetime_picker :publish_datetime
#・・・
end
- Question管理画面を編集
just-datetime-pickerを使うとpermit_paramsの内容が一部変わります。
publish_datetimeの項目が分割され、
publish_datetime_date
publish_datetime_time_hour
publish_datetime_time_minute
の3項目が必要になります。
そして、publish_datetimeのinput設定時に、 as: :just_datetime_picker を指定しておきます。
具体的には以下のようになります。
# app/admin/question.rb
ActiveAdmin.register Question do
permit_params :title, :image, :image_cache, :publish_datetime_date, :publish_datetime_time_hour, :publish_datetime_time_minute
form(:html => { :multipart => true }) do |f|
f.inputs "Details" do
f.input :title
f.input :image
f.input :publish_datetime, as: :just_datetime_picker
f.input :image_cache, as: :hidden
end
f.actions
end
end
画像アップロードに関してですが、image_cacheを設定しておくことで、画像ファイル選択後に他の項目のバリデーションに失敗した場合、キャッシュから読み込んでくれるので画像再選択をする必要がなくなります。(hiddenタグも追加で必要になります。)
そして、画像アップロードの際にはmultipartをtrueに設定しておく必要もあります。
この状態で管理画面に再度アクセスするとカレンダーから日付選択が可能になっています。
4-3. Question(親)作成時に、併せてAnswer(子)のデータも登録
最後はQuestionデータ作成時に合わせてAnswerも登録できるようにします。
Question(親)からAnswer(子)を作成する場合はmodelにaccepts_nested_attributes_forを設定します。
# app/models/question.rb
class Question < ActiveRecord::Base
has_many :answers, dependent: :destroy
accepts_nested_attributes_for :answers, allow_destroy: true
mount_uploader :image, ImageUploader
just_define_datetime_picker :publish_datetime
#・・・
end
allow_destroyをtrueにするとAnswer(子)の削除も可能になります。
続いて、管理画面を修正します。
permit_paramsにanswers_attributesを追加し、更新可能なAnswerの項目を配列で記述します。
_destroyは削除のための項目です。
inputsにもAnswer用の項目を追加しています。
# app/admin/question.rb
ActiveAdmin.register Question do
permit_params :title, :image, :image_cache, :publish_datetime_date, :publish_datetime_time_hour, :publish_datetime_time_minute, answers_attributes: [:id, :answer_text, :total_count, :_destroy]
form(:html => { :multipart => true }) do |f|
f.inputs "Details" do
f.input :title
f.input :image
f.input :publish_datetime, as: :just_datetime_picker
f.input :image_cache, as: :hidden
end
f.inputs do
f.has_many :answers, heading: 'Answers', allow_destroy: true, new_record: true do |a|
a.input :answer_text
a.input :total_count
end
end
f.actions
end
end
これでQuestion作成時にAnswerを追加、削除できるようになりました。
5. さらにカスタマイズ
管理画面の一覧ページをカスタマイズする際に、他のモデルの項目を呼び出した場合、N+1問題が発生します。
その場合はscoped_collectionメソッドをオーバーライドしてincludesを追加します。
# app/admin/question.rb
ActiveAdmin.register Question do
# ・・・
controller do
def scoped_collection
Question.includes(:answers)
end
end
# ・・・
end
これで、最初にお見せした管理画面の状態になりました。
これならそれなりに使えそうです。
さらにindexやshowをカスタマイズしたのですが、そちらに関しては直接リポジトリを見ていただければと思います。
hilotter/rails_question_sample
まだまだ色々カスタマイズできそうですが、長くなってしまったので今回は以上です。
参考
- 【Rails4】active_admin+PaperClipでhas_manyなform - avosalmonのブログ
- ruby on rails - ActiveAdmin nested form on #show page - Stack Overflow
- ruby on rails - Displaying has_many through association as column in Active Admin index - Stack Overflow
- [[Rails 4.x] ActiveAdmin をカスタマイズして、indexやshowでassociation関係のテーブルのカラムも表示させる - Qiita](http://qiita.com/tsumekoara/items/84f87fb8bd85f625129e)
- Preventing N+1 Query Problem in Active Admin index action. - ravelllの日記