This post is Private. Only a writer or those who know its URL can access this post.

Article information
Show article in Markdown
Report article
Help us understand the problem. What is going on with this article?

Ruby on RailsでCRUD操作が出来るタスク管理アプリケーション構築 Part4

Part3で構築したRailsアプリケーションに操作制限を追加実装.

■ タスク管理制御.

コントローラ内にてフィルタ機能(コールバック)でアクション処理前に任意処理が行えるよう制御.

各アクションの前に処理されるフィルダでログイン状態を確認し、ログインしていなければログイン画面にリダイレクトさせ、実質的にタスク管理機能が利用出来ないように制御.

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  helper_method :current_user
  before_action :login_required

  private

  def current_user
    @current_user ||= User.find_by(id: session[:user_id]) if session[:user_id]
  end

  # アクション処理前にユーザーのログイン状態かチェックを実施し、未ログインの場合はログイン画面へリダイレクト.
  def login_required
    redirect_to login_path unless current_user
  end
end

ただしログイン画面の表示時には、同チェックが実行されないよう制御.

class SessionController < ApplicationController
  skip_before_action :login_required
  ...
end

特定のコントローラにおいて、親クラスなど既に定義済みのフィルタを通らせないようにするためには、skip_before_actionを利用.

■ ログインユーザーのデータのみ扱う.

ログイン中のユーザーに紐づいたタスクデータだけを扱えるように制御.

  1. UserとTaskを紐付ける(tasksテーブルにuser_idカラムを追加し、タスク所有のユーザーのidが格納されるよう制御).
  2. Railsの「関連」を定義.
  3. ログインユーザーに紐づいたTaskデータを登録できるように制御.
  4. 一覧、詳細、変更など既存レコードを扱う機能はログインユーザーに紐づくデータのみを扱う.
bin/rails g migration AddUserIdToTasks

TaskとUserが紐づくように書き換え.

db/migrate/yyyymmddhhmmss_add_user_id_to_tasks.rb
class AddUserIdToTasks < ActiveRecord::Migration[5.2]
  def up
    execute 'DELETE FROM tasks;'
    add_reference :tasks, :user, null: false, index: true
  end

  def down
    remove_reference :tasks, :user, index: true
  end
end

Railsには「関連(Association)」という仕組みが用意されており、データベースでの紐付きを前提にモデルクラス同士の紐付けを定義することが可能で、UserとTaskの関係は1対多なので、以下のように定義できる.

app/models/user.rb
class User < ApplicationRecord
  has_secure_password

  validates :name, presence: true
  validates :email, presence: true, uniqueness: true

  has_many :tasks
end

has_manyではクラス(Userクラス)のidを外部キーとして抱える他のクラス(Task)があり、そのレコードが複数件登録可能であることを表現.

app/models/task.rb
class Task < ApplicationRecord
  before_validation :set_nameless_name
  validates :name, presence: true
  validates :name, length: { maximum: 30 }
  validate :validate_name_not_including_comma

  belongs_to :user

  private
  ...
end

belong_toでは、クラス(Task)が別のクラス(User)に従属し、従属先のクラスのidを外部キーとして抱えていることを表現.

app/controllers/tasks_controller.rb
class TasksController < ApplicationController
  def index
    @tasks = current_user.tasks
  end

  def show
    @task = current_user.tasks.find(params[:id])
  end

  def new
    @task = Task.new
  end

  def edit
    @task = current_user.tasks.find(params[:id])
  end

  def update
    task = current_user.tasks.find(params[:id])
    task.update!(task_params)
    redirect_to tasks_url, notice: "タスク「#{task.name}」を更新しました。"
  end

  def create
    @task = Task.new(task_params.merge(user_id: current_user.id))
    if @task.save
      redirect_to @task, notice: "タスク「#{@task.name}」を登録しました。"
    else
      render :new
    end
  end

  def destroy
    task = current_user.tasks.find(params[:id])
    task.destroy
    redirect_to tasks_url, notice: "タスク「#{task.name}」を削除しました。"
  end

  private

  def task_params
    params.require(:task).permit(:name, :description)
  end
end

ログイン中のユーザーを指定するように修正.

■ 管理者利用制御.

管理者権限を持つユーザーのみユーザー管理機能が使えるように実装.

app/views/layouts/application.html.slim
doctype html
html
  head
    title
      | Taskleaf
    = csrf_meta_tags
    = csp_meta_tag
    = stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload'
    = javascript_include_tag 'application', 'data-turbolinks-track': 'reload'
  body
    .app-title.navbar-expamd-md.navbar-light.bg-light
      .navbar-brand Taskleaf

    ul.navbar-nav.ml-auto
      - if current_user
        li.nav-item= link_to 'タスク一覧', tasks_path, class: 'nav-link'
        - if current_user.admin?
          li.nav-item= link_to 'ユーザー一覧', admin_users_path, class: 'nav-link'
        li.nav-item= link_to 'ログアウト', logout_path, method: :delete, class: 'nav-link'
      - else
        li.nav-item= link_to 'ログイン', login_path, class: 'nav-link'
    .container
      - if flash.notice.present?
        .alert.alert-success = flash.notice
      = yield

ログインユーザーが管理権限を持っている場合のみ、ナビゲーションバーにユーザー一覧リンクが表示される.

管理者権限以外は利用を禁止するフィルタとしてrequire_adminを追加.

class Admin::UsersController < ApplicationController
  before_action :require_admin
  ...
  private
  ...
  def require_admin
    redirect_to root_path unless current_user.admin?
  end
end

権限がない場合はトップ画面へリダイレクトさせるよう制御.

■ 最初のユーザー作成.

Railsコンソールで最初の管理者を作成.

> User.create!(name: 'admin', email: 'admin@example.com', password: 'password', password_confirmation: 'password', admin: true)

■ 参考文献.

:books: 現場で使える Ruby on Rails 5速習実践ガイド.

machio77777
プロジェクトマネージャー / 最近はマネージメント業務や、クライアント折衝がメインのため、業務でコードは書きませんが、プライベートで調べたことをメモ書きレベルで投稿しています。
https://tana-labo.tokyo/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした