Masahiro Okubo

初心者向け : Railsログイン機能をつけてQAサイトを作る 5 -管理画面機能-

初心者向け : Railsログイン機能をつけてQAサイトを作る 5 -管理画面機能-

※ rails6に対応させました

初心者向け : Railsログイン機能をつけてQAサイトを作る 1 -ログイン機能+質問機能-
初心者向け : Railsログイン機能をつけてQAサイトを作る 2 -Bootstrap+UI修正-
初心者向け : Railsログイン機能をつけてQAサイトを作る 3 -回答機能+リアクション機能+ベストアンサー機能-
初心者向け : Railsログイン機能をつけてQAサイトを作る 4 -タグ付け機能-
初心者向け : Railsログイン機能をつけてQAサイトを作る 5 -管理画面機能-
初心者向け : Railsログイン機能をつけてQAサイトを作る 6 -検索機能-

今回は管理画面を作成します

管理画面は管理者ユーザーのみアクセスすることができ、
データベースの操作も行うことができるので非常に便利な機能です

Gemの追加

管理画面機能は何種類かのgemがありますが、
今回はactiveadminを利用します
※Gemfileを全てここに記載していますが、デプロイをしていたり、
productionなどの設定を行なっている場合は、
様々なエラーが起こる可能性があるのでactiveadminのみを追加してください。

source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '2.6.3'

gem 'rails', '~> 6.0.3', '>= 6.0.3.3'
gem 'sqlite3', '~> 1.4'
gem 'puma', '~> 4.1'
gem 'sass-rails', '>= 6'
gem 'webpacker', '~> 4.0'
gem 'turbolinks', '~> 5'
gem 'jbuilder', '~> 2.7'
gem 'bootsnap', '>= 1.4.2', require: false

gem 'devise' # ログイン機能実装のためのdeviseというgemを追加
gem 'activeadmin' # 管理画面機能

group :development, :test do
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
end

group :development do
gem 'web-console', '>= 3.3.0'
gem 'listen', '~> 3.2'
gem 'spring'
gem 'spring-watcher-listen', '~> 2.0.0'
end

group :test do
gem 'capybara', '>= 2.15'
gem 'selenium-webdriver'
gem 'webdrivers'
end

gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

Gemfileの修正が完了したらターミナルでこちらを実行

$ bundle install

次はこちらをターミナルで実行

$ rails g active_admin:install

その後完了したらこちらも実行

$ rails db:migrate

こちらで準備は完了しましたが、前回の作成したTagなどの情報も
全てdb/seeds.rbファイルに反映させましょう

反映後はこのようなファイルになります

# ユーザーのデータ
User.create(
    [
        {name: '鈴木太郎', email: 'sitsumon1@gmail.com', role: '質問者', password: '11111111', password_confirmation: '11111111'},
        {name: '佐々木太郎', email: 'sitsumon2@gmail.com', role: '質問者', password: '11111111', password_confirmation: '11111111'},
        {name: '田中太郎', email: 'sitsumon3@gmail.com', role: '質問者', password: '11111111', password_confirmation: '11111111'},
        {name: '高橋花子', email: 'kaitou1@gmail.com', role: '回答者', password: '11111111', password_confirmation: '11111111'},
        {name: '斎藤花子', email: 'kaitou2@gmail.com', role: '回答者', password: '11111111', password_confirmation: '11111111'},
        {name: '池田花子', email: 'kaitou3@gmail.com', role: '回答者', password: '11111111', password_confirmation: '11111111'}])

Question.create(
    [
        {title: '東京のお土産で喜ばれるもの', body: '今週末東京に行きますが、東京土産で喜ばれるものありますか?', user_id: 1},
        {title: '好きな動物', body: '一番好きな動物は何ですか?', user_id: 1},
        {title: '美味しいご飯や', body: '名古屋周辺の美味しいご飯屋さん教えてください', user_id: 2},
        {title: '明日の天気', body: '明日の天気は?', user_id: 2},
        {title: '読書時間について', body: '一日の読書時間はどれぐらい?', user_id: 3},
        {title: '英語の勉強法', body: '英語はどうやって勉強したらいいですか?', user_id: 3}])

Answer.create(
    [
        {body: '東京ばな奈じゃないですか?', question_id: 1, user_id: 4},
        {body: 'ラスクみたいなやつ', question_id: 1, user_id: 5},
        {body: '中華まん', question_id: 1, user_id: 6},
        {body: '猫', question_id: 2, user_id: 4},
        {body: '犬', question_id: 2, user_id: 5},
        {body: 'とり', question_id: 2, user_id: 6},
        {body: '矢場とん', question_id: 3, user_id: 4},
        {body: '山ちゃん', question_id: 3, user_id: 5},
        {body: 'みせん', question_id: 3, user_id: 6},
        {body: '晴れ', question_id: 4, user_id: 4},
        {body: '曇り', question_id: 4, user_id: 5},
        {body: '雨', question_id: 4, user_id: 6},
        {body: '1時間', question_id: 5, user_id: 4},
        {body: '2時間', question_id: 5, user_id: 5},
        {body: '3時間', question_id: 5, user_id: 6},
        {body: '特かく話す', question_id: 6, user_id: 4},
        {body: '単語の勉強', question_id: 6, user_id: 5},
        {body: 'レッスンに行く', question_id: 6, user_id: 6}])

Reaction.create(
    [
        {body: '東京ばな奈いいですね', user_id: 1, answer_id: 1},
        {body: '猫いいですね!', user_id: 1, answer_id: 4},
        {body: '矢場とんいいですね!', user_id: 3, answer_id: 7}])


Tag.create(
    [
        {name: '動物'},
        {name: 'スポーツ'},
        {name: 'ご飯'},
        {name: 'その他'}])

# activeadmin(管理画面)に入るためのユーザーです。ターミナルで「rails g active_admin:install」を実行後
# 作成されます。メールアドレスとパスワードはわかりやすく設定していますが、必要であれば変えてください。
AdminUser.create!(email: 'admin@gmail.com', password: '11111111', password_confirmation: '11111111') if Rails.env.development?

手間ですが、db/development.sqlite3を削除してもう一度データベースを作り直します

削除が完了したらターミナルでこちらを実行

$ rails db:migrate

データベースの作成が完了したら、seeds.rbのデータを入れます

$ rails db:seed

これで大方準備は完了したので、次は確認していきます

まずはrailsを起動

$ rails s

起動後はこちらのリンクから管理画面にいきます

http://localhost:3000/admin/login

リンクではこのような画面が表示されます

フォームには先ほどdb/seeds.rbに追加したAdminUserの情報でログインします

ログインが完了するとこのような画面が表示されます

タイムゾーンと言語を変更する

config/application.rbを以下のように変更してください

require_relative 'boot'

require 'rails/all'

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module QaSite
  class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 6.0
    config.time_zone = 'Tokyo'
    config.i18n.default_locale = :ja
  end
end

時間を東京時間へ合わせたのと、言語を日本語へ変更したので、
管理画面は日本語へと変更されました

次はconfig/localesに
devise.ja.ymlファイルを作成し以下の内容をコピペしてください
deviseが大方日本語になります

ja:
  activerecord:
    errors:
      models:
        user:
          attributes:
            email:
              taken: "は既に使用されています。"
              blank: "が入力されていません。"
              too_short: "は%{count}文字以上に設定して下さい。"
              too_long: "は%{count}文字以下に設定して下さい。"
              invalid: "は有効でありません。"
            password:
              taken: "は既に使用されています。"
              blank: "が入力されていません。"
              too_short: "は%{count}文字以上に設定して下さい。"
              too_long: "は%{count}文字以下に設定して下さい。"
              invalid: "は有効でありません。"
              confirmation: "が内容とあっていません。"
    attributes:
      user:
        current_password: "現在のパスワード"
        name: 名前
        email: "メールアドレス"
        password: "パスワード"
        password_confirmation: "確認用パスワード"
        remember_me: "次回から自動的にログイン"
    models:
      user: "ユーザ"
  devise:
    confirmations:
      new:
        resend_confirmation_instructions: "アカウント確認メール再送"
    mailer:
      confirmation_instructions:
        action: "アカウント確認"
        greeting: "ようこそ、%{recipient}さん!"
        instruction: "次のリンクでメールアドレスの確認が完了します:"
      reset_password_instructions:
        action: "パスワード変更"
        greeting: "こんにちは、%{recipient}さん!"
        instruction: "誰かがパスワードの再設定を希望しました。次のリンクでパスワードの再設定が出来ます。"
        instruction_2: "あなたが希望したのではないのなら、このメールは無視してください。"
        instruction_3: "上のリンクにアクセスして新しいパスワードを設定するまで、パスワードは変更されません。"
      unlock_instructions:
        action: "アカウントのロック解除"
        greeting: "こんにちは、%{recipient}さん!"
        instruction: "アカウントのロックを解除するには下のリンクをクリックしてください。"
        message: "ログイン失敗が繰り返されたため、アカウントはロックされています。"
    passwords:
      edit:
        change_my_password: "パスワードを変更する"
        change_your_password: "パスワードを変更"
        confirm_new_password: "確認用新しいパスワード"
        new_password: "新しいパスワード"
      new:
        forgot_your_password: "パスワードを忘れましたか?"
        send_me_reset_password_instructions: "パスワードの再設定方法を送信する"
    registrations:
      edit:
        are_you_sure: "本当に良いですか?"
        cancel_my_account: "アカウント削除"
        currently_waiting_confirmation_for_email: "%{email} の確認待ち"
        leave_blank_if_you_don_t_want_to_change_it: "空欄のままなら変更しません"
        title: "%{resource}編集"
        unhappy: "気に入りません"
        update: "更新"
        we_need_your_current_password_to_confirm_your_changes: "変更を反映するには現在のパスワードを入力してください"
      new:
        sign_up: "アカウント登録"
    sessions:
      new:
        sign_in: "ログイン"
    shared:
      links:
        back: "戻る"
        didn_t_receive_confirmation_instructions: "アカウント確認のメールを受け取っていませんか?"
        didn_t_receive_unlock_instructions: "アカウントの凍結解除方法のメールを受け取っていませんか?"
        forgot_your_password: "パスワードを忘れましたか?"
        sign_in: "ログイン"
        sign_in_with_provider: "%{provider}でログイン"
        sign_up: "アカウント登録"
    unlocks:
      new:
        resend_unlock_instructions: "アカウントの凍結解除方法を再送する"

ついでにja.ymlも作成します
config/localesに
ja.ymlファイルを作成し以下の内容をコピペしてください
こちらはアプリ全体の日本語設定です

ja:
  activerecord:
    errors:
      messages:
        record_invalid: "バリデーションに失敗しました: %{errors}"
        restrict_dependent_destroy:
          has_one: "%{record}が存在しているので削除できません"
          has_many: "%{record}が存在しているので削除できません"
  date:
    abbr_day_names:
    - 日
    - 月
    - 火
    - 水
    - 木
    - 金
    - 土
    abbr_month_names:
    -
    - 1月
    - 2月
    - 3月
    - 4月
    - 5月
    - 6月
    - 7月
    - 8月
    - 9月
    - 10月
    - 11月
    - 12月
    day_names:
    - 日曜日
    - 月曜日
    - 火曜日
    - 水曜日
    - 木曜日
    - 金曜日
    - 土曜日
    formats:
      default: "%Y/%m/%d"
      long: "%Y年%m月%d日(%a)"
      short: "%m/%d"
    month_names:
    -
    - 1月
    - 2月
    - 3月
    - 4月
    - 5月
    - 6月
    - 7月
    - 8月
    - 9月
    - 10月
    - 11月
    - 12月
    order:
    - :year
    - :month
    - :day
  datetime:
    distance_in_words:
      about_x_hours:
        one: 約1時間
        other: 約%{count}時間
      about_x_months:
        one: 約1ヶ月
        other: 約%{count}ヶ月
      about_x_years:
        one: 約1年
        other: 約%{count}年
      almost_x_years:
        one: 1年弱
        other: "%{count}年弱"
      half_a_minute: 30秒前後
      less_than_x_minutes:
        one: 1分以内
        other: "%{count}分未満"
      less_than_x_seconds:
        one: 1秒以内
        other: "%{count}秒未満"
      over_x_years:
        one: 1年以上
        other: "%{count}年以上"
      x_days:
        one: 1日
        other: "%{count}日"
      x_minutes:
        one: 1分
        other: "%{count}分"
      x_months:
        one: 1ヶ月
        other: "%{count}ヶ月"
      x_seconds:
        one: 1秒
        other: "%{count}秒"
    prompts:
      day: 日
      hour: 時
      minute: 分
      month: 月
      second: 秒
      year: 年
  errors:
    format: "%{attribute}%{message}"
    messages:
      accepted: を受諾してください
      blank: を入力してください
      present: は入力しないでください
      confirmation: と%{attribute}の入力が一致しません
      empty: を入力してください
      equal_to: は%{count}にしてください
      even: は偶数にしてください
      exclusion: は予約されています
      greater_than: は%{count}より大きい値にしてください
      greater_than_or_equal_to: は%{count}以上の値にしてください
      inclusion: は一覧にありません
      invalid: は不正な値です
      less_than: は%{count}より小さい値にしてください
      less_than_or_equal_to: は%{count}以下の値にしてください
      model_invalid: "バリデーションに失敗しました: %{errors}"
      not_a_number: は数値で入力してください
      not_an_integer: は整数で入力してください
      odd: は奇数にしてください
      required: を入力してください
      taken: はすでに存在します
      too_long: は%{count}文字以内で入力してください
      too_short: は%{count}文字以上で入力してください
      wrong_length: は%{count}文字で入力してください
      other_than: は%{count}以外の値にしてください
    template:
      body: 次の項目を確認してください
      header:
        one: "%{model}にエラーが発生しました"
        other: "%{model}に%{count}個のエラーが発生しました"
  helpers:
    select:
      prompt: 選択してください
    submit:
      create: 登録する
      submit: 保存する
      update: 更新する
  number:
    currency:
      format:
        delimiter: ","
        format: "%n%u"
        precision: 0
        separator: "."
        significant: false
        strip_insignificant_zeros: false
        unit: 円
    format:
      delimiter: ","
      precision: 3
      separator: "."
      significant: false
      strip_insignificant_zeros: false
    human:
      decimal_units:
        format: "%n %u"
        units:
          billion: 十億
          million: 百万
          quadrillion: 千兆
          thousand: 千
          trillion: 兆
          unit: ''
      format:
        delimiter: ''
        precision: 3
        significant: true
        strip_insignificant_zeros: true
      storage_units:
        format: "%n%u"
        units:
          byte: バイト
          gb: GB
          kb: KB
          mb: MB
          tb: TB
    percentage:
      format:
        delimiter: ''
        format: "%n%"
    precision:
      format:
        delimiter: ''
  support:
    array:
      last_word_connector: と
      two_words_connector: と
      words_connector: と
  time:
    am: 午前
    formats:
      default: "%Y/%m/%d %H:%M:%S"
      long: "%Y年%m月%d日(%a) %H時%M分%S秒 %z"
      short: "%y/%m/%d %H:%M"
    pm: 午後

Modelを管理画面で操作できるようにする

管理画面は作成し終わりましたが、
まだデータを管理することができません

なので、次は作成したModelを利用して
データベースにアクセスできるようにします

まずはQuestion modelからいきます
ターミナルでこちらを実行

$ rails g active_admin:resource question

完了すると、app/adminにquestions.rbというファイルが生成されます

それでは、railsを起動させてこちらにアクセスしてみましょう

http://localhost:3000/admin/questions

アクセス後はこのような画面になっています

次は残りのModelを追加していきます
※question_tagですが、あくまでも中間テーブルなので
追加していません。

$ rails g active_admin:resource answer
$ rails g active_admin:resource reaction
$ rails g active_admin:resource tag
$ rails g active_admin:resource user

全て追加後はこのような画面になっています

最後にQuestionからTagを操作できるようにして完了させます

app/admin/quesitons.rbをこのように修正します

ActiveAdmin.register Question do

  # 閲覧、編集、削除をコントロールすることができる.
  # ユーザー情報など消えたら問題になるModelは制限を加えたりすることがある
  actions :all
  config.per_page = 20


  #showページ
  show do
    attributes_table do
      row :title
      row :body
      row :best_answer_id
      row :tags do
        question.tags.collect { |n| n.name }.join(', ')
      end
      
    end
  end

  #formのコード
  form do |f|
    f.inputs '質問' do
      f.input :title
      f.input :body
      f.input :best_answer_id
      f.has_many :question_tags, allow_destroy: true, new_record: true do |t|
        t.input :tag_id,
                label: 'タグ',
                as: :select,
                collection: Tag.all
      end
      f.actions
    end
  end
  permit_params :title,
                :body,
                :best_answer_id,
                question_tags_attributes: [:id, :tag_id, :_destroy]

end

次はapp/models/question.rbをこのように修正

class Question < ApplicationRecord
  belongs_to :user
  has_many :answers
  has_many :question_tags
  has_many :tags , through: :question_tags
  accepts_nested_attributes_for :question_tags, :allow_destroy => true
end

修正後のQuestion編集、追加画面ではtagが追加できるようになっています

閲覧でもこのようにタグが表示されています

まとめ

activeadminは簡単に管理画面機能を作成することができて、
拡張もそこそこできるので早くアプリを作りたいという場合は非常に有効です。

また本来であればデータベース情報にアクセスするには
データベースクライアントというデータベースの中身を確認する
ツールが必要です。
しかしactiveadminを利用すればMySQLやSQLiteなど気にすることなく
利用が可能なので初心者向けで選択してみました

ただし、今回使った機能は1から作ろうと思うとそこそこ大変なものもあるので、
あくまでも初心者、起業するためのツールとして、というのが前提にあります

本格的にエンジニアになるという目標があるのであれば、
ここからはgemに頼るのではなく自力で作ることも重要となります

以上、!

参考記事

rails-i18n

deviseを日本語化する

Buy Me A Coffee

関連記事

copyright© 2016-2021 Masahiro Okubo