バックエンド

ActiveAdminでちょっと使い勝手の良い管理画面を作るTips

shimo

最近はJavaでバッチ処理を書いていることが多い下條です。自分の中でエンタープライズJava熱が上がってきました。なので、Javaの話題でもいいんですけど、でも僕はRails。

Ruby on Railsで簡単に管理画面を作りたいときに使えるgemであるActiveAdmin。管理画面は一般的にあまり見た目の素敵さは要求されないことが多く、ActiveAdminである程度のレベルの管理画面が簡単に作ることができる。ただ、本当にデフォルトのままだととても使いにくい管理画面になったりする。問題としては例えば、

  • モデルのCRUDをそのまま実現するだけでは不便な管理フローとなる場合がある(多対多の中間モデルのデータ登録など)
  • 日本語化されていない

などなど。今回は、簡単なサンプルアプリを作りながらこれらを改善していってみる。


簡単な例として、書籍(Book)と著者(Author)を管理するアプリを作っていきながら見ていくことにする。モデルは以下。BookとAuthorはお互いにhas_manyの関係になっており、中間モデルが存在する例とする。

1.png

モデルや、マイグレーションファイル作成の手順はこんな感じで。

$ rails g model Author name:string description:text
$ rails g model Book title:string isbn:string published_date:date
$ rails g model AuthorBook author_id:integer book_id:integer
$ rake db:migrate

それぞれのモデルにhas_manyとbelongs_toなどを追加。例えばapp/model/book.rbはこんな感じ。(バリデーションなんかはサボってます。)

class Book < ActiveRecord::Base
  has_many :author_books, dependent: :destroy
  has_many :authors, through: :author_books
  accepts_nested_attributes_for :author_books, allow_destroy: true
end

さっそくActiveAdmin導入。Gemfileに以下を追加し、

gem 'activeadmin', github: 'activeadmin'
gem 'inherited_resources', github: 'josevalim/inherited_resources', branch: 'rails-4-2'

bundle installした後、

$ rails g active_admin:install --skip-users

とりあえず今回は簡単のため、'--skip-users'オプションを追加したため、ユーザなしで使える。

http://localhost:3000/admin

にアクセスすると早速管理画面ができている。
※config/initializers/adtive_admin.rbで
config.current_user_method = :current_admin_user
をコメントアウトしておく。

2.png

ここで、とりあえず全てのモデルをActiveAdminの管理対象に加えてみる。

$ rails g active_admin:resource author
$ rails g active_admin:resource book
$ rails g active_admin:resource author_book

結果、app/admin/author.rb, app/admin/book.rb, app/admin/author_book.rbのファイルが追加される。
あとは、Strong Parameters対応で、permit_paramsを設定しておく。(たぶん各ファイルにサンプルが書かれていると思う。)

これでひと通りのCRUD操作ができるようになった。さすがActiveAdmin。早い。例えば書籍の登録画面は以下のようになる。

3.png

多対多の中間モデルのデータ登録

しかし、問答無用でモデルをActiveAdminでの管理対象にすると、不便な管理フローとなってしまう場合がある。多対多の中間モデルはその代表例である。
ActiveAdminはモデルをもとに、そのメンバを登録、編集、削除できるようにするだけなので、多対多の中間モデルをActiveAdminでの管理対象にすると、IDを2つ登録するUIとなる。
例えば今回のサンプルでは、書籍と著者を結びつける登録画面は以下のようになる。

4.png

うーむ。日本語化されてないというところはおいておいて、これだと、書籍と著者を紐付けるのに、まず書籍IDと著者IDを調べてから、この画面でIDを入れるという、おそらく非常に面倒な手順となる。
ここは、書籍の登録時にでも、その著者を複数登録できるようにしたいところである。
そこで、書籍の登録画面を変更してみる。

app/admin/book.rb
でフォームをカスタマイズする。
以下のようにフォームのカスタム設定を追加。あとは、permit_paramsも追加。

ActiveAdmin.register Book do
  permit_params :title, :isbn, :published_date, author_books_attributes: [:author_id, :_destroy, :id]

  form do |f|
    f.inputs '書籍登録' do
      f.input :title
      f.input :isbn
      f.input :published_date
      f.has_many :author_books, allow_destroy: true, heading: false,
                                new_record: true do |ab|
        ab.input :author_id,
                 label: '著者',
                 as: :select,
                 collection: Author.all.map { |a| [a.name, a.id] }
      end
      f.actions
    end
  end
end

すると、こんな感じでフォームができ、書籍の登録、編集時に、紐付ける著者を著者名で登録できるようになり、ちょっと便利になった。

5.png

ただ、これだけだと登録完了画面には著者名が出てこない。

6.png

ついでに、この画面にcreated_atとupdate_atカラムの情報が出ているのもちょっと邪魔である。というわけで、それらを削除しつつ、著者名を追加してみる。

再びapp/admin/book.rbでカスタマイズ。今度は以下を追加。

  show do
    attributes_table do
      row 'タイトル' do
        resource.title
      end
      row 'ISBN' do
        resource.isbn
      end
      row '出版日' do
        resource.published_date
      end
      row '著者' do
        Author.where(id: AuthorBook.where(book_id: resource.id).all.pluck(:author_id)).all.pluck(:name).join(',')
      end
    end

結果。

7.png

だいぶマシになった。

また、多対多の中間モデル以外でも、モデル設計によってはそのままActiveAdminで管理すると使い勝手が悪くなる場合が多々あるので、モデル設計が重要になるのと同時に、もし既存モデルをActiveAdminで管理したいという時にはどういうUIになるのかよく考えたほうがよいと思われる。まあ、ActiveAdminに合わせるというのは本末転倒だけれども。

日本語化

上記で局所的に日本語化していたが、ちゃんと日本語をするにはI18nを使う。

まずはconfig/locales/ja.ymlを作成。

https://github.com/svenfuchs/rails-i18n/blob/master/rails/locale/ja.yml

ここらへんからもってくるとよい。そうすると、こんな感じ。

8.png

これだと「Book を作成する」とみたいにちょっとルー大柴的になっているので、モデルの日本語化をする。また、ついでに以下などの対応もやってみる。

  • 一覧のCreated At, Updated Atのカラムを見せたくないので削除
  • 検索条件のAuthor Books、Created At, Updated Atを削除
  • メニューの「Author Books」は削除:app/admin/author_book.rbを削除。

最終的なapp/admin/book.rbは以下。

ActiveAdmin.register Book do
  permit_params :title, :isbn, :published_date, author_books_attributes: [:author_id, :_destroy, :id]
  filter :authors_name_eq, label: I18n.t('activerecord.attributes.author.name'), as: :select, collection: Author.all.map { |a| [a.name, a.name] } # ここの日本語化ができなかったのでちょっと強引にフィルターを入れてます。
  filter :title
  filter :isbn
  filter :published_date

  index title: I18n.t('activerecord.models.book') + '一覧' do
    column I18n.t('activerecord.attributes.book.title'), :title
    column I18n.t('activerecord.attributes.book.isbn'), :isbn
    column I18n.t('activerecord.attributes.book.published_date'), :published_date
    column I18n.t('activerecord.attributes.author.name') do |book|
      Author.where(id: AuthorBook.where(book_id: book.id).all.pluck(:author_id)).all.pluck(:name).join(', ')
    end
    actions
  end

  form do |f|
    f.inputs I18n.t('activerecord.models.book') + '登録' do
      f.input :title
      f.input :isbn
      f.input :published_date
      f.has_many :author_books, allow_destroy: true, heading: false,
                                new_record: true do |ab|
        ab.input :author_id,
                 label: I18n.t('activerecord.attributes.author.name'),
                 as: :select,
                 collection: Author.all.map { |a| [a.name, a.id] }
      end
      f.actions
    end
  end

  show do
    attributes_table do
      row I18n.t('activerecord.attributes.book.title') do
        resource.title
      end
      row I18n.t('activerecord.attributes.book.isbn') do
        resource.isbn
      end
      row I18n.t('activerecord.attributes.book.published_date') do
        resource.published_date
      end
      row I18n.t('activerecord.attributes.author.name') do
        Author.where(id: AuthorBook.where(book_id: resource.id).all.pluck(:author_id)).all.pluck(:name).join(', ')
      end
    end
  end
end

その結果の画面がこちら。

9.png

だいぶマシになったのではないだろうか。

いろいろとまだまだ改善・機能追加などできるが、キリがないのでとりあえず今回は主にフォーム・日本語化についてご紹介した。

まとめ

他にも簡単にカスタマイズできる部分はいろいろとある。ActiveAdminはユーザが多いため、情報がたくさんネット上に転がっているから、実現したい機能で検索すればやり方が出てくることも多い。

ただし、ここからが一番強調したいところなのだが、
今回見てきたActiveAdminの機能を使ったカスタマイズは簡単な例で、結局ごりごりと自分でビュー、コントローラを書かないといけないような場合もある。例えば「登録の確認画面を出す」場合。これは自分でビュー、コントローラを書かないといけない。ActiveAdminでも**がんばれば**なんでもできるのは確かだが、こういったごりごりカスタマイズが増えてくるとActiveAdminを使う意味が薄れてくる。

そして、前提として、管理画面を作ろうとするときに、ActiveAdminありきで考えないということは大事である。ActiveAdminありきで考えてしまうとActiveAdminが標準で提供している機能ベースで機能設計を考えてしまいがちになる。大事なのは、お客様が管理画面に何を望んでいるか、何が必要なのかをまずしっかりと考え、その上でActiveAdminが適しているかどうかを判断することであろう。その際にはActiveAdminで簡単にできること、できないことを知っておくことが重要なのだが、私もまだ勉強中の身なのであまり大それたことは言えない。今後とも調べていこうと思っている。

Ruby on Railsを活用したWebサービスや業務システム開発をご検討の企業様は、是非MMMにご相談下さいませ!

AUTHOR
shimo
shimo
記事URLをコピーしました