[rails][まとめ]Active Adminをカスタマイズしていい感じの管理画面をつくる


[まとめ] 現在開催中のKindleセール情報はこちら

Ruby2.1.4, Rails4.1.7で確認。

Railsで管理画面をつくる際にActive Adminを使うことが多いのですが、毎回手順を調べなおしたりしていたので、ひと通りのカスタマイズ方法をまとめておきます。

結構長くなってしまいました。

今回はサンプルとして、質問と質問に対する回答データを管理画面上で追加、閲覧、編集、削除できるようにしてみます。

今回のカスタマイズでできるようになることは

  • carrierwaveで画像アップロード
  • 日付入力の際にカレンダーから日付選択
  • Question(親)作成時に、併せてAnswer(子)のデータも登録

の3つです。

admin7

最終的に↑の感じの管理画面になります。

テーブル構造は以下のようにします。

erd

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/以下に設置しておきます。

これで基本的な管理画面設定ができました。

admin1

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

この状態で管理画面にアクセスすると画像アップロードフォームに切り替わっています。

admin2

ただ、このままだとStrongParameters設定がないためエラーになります。

admin3

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の入力が全てセレクトボックスになっているのをカレンダーから日付選択できるようにします。

admin4

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に設定しておく必要もあります。

この状態で管理画面に再度アクセスするとカレンダーから日付選択が可能になっています。

admin5

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を追加、削除できるようになりました。

admin6

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

まだまだ色々カスタマイズできそうですが、長くなってしまったので今回は以上です。

参考

[まとめ] 現在開催中のKindleセール情報はこちら