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操作が出来るタスク管理アプリケーション構築 Part2

Part1で構築したRailsアプリケーションに以下機能を実装.

:small_blue_diamond: マイグレーション基礎.
:small_blue_diamond: データ内容の制限.
:small_blue_diamond: モデルの検証.
:small_blue_diamond: コールバック.

■ マイグレーション基礎.

マイグレーション機能はテーブルやカラム、インデックス追加/削除、既存データ修正を楽に行える機能.

■ マイグレーション機能の基本的な2ステップ.

:small_blue_diamond: DB構造を変更するコードをRubyで記述したマイグレーションファイルを作成.
:small_blue_diamond: マイグレーションファイルをrails db:migrateでデータベースに適用.

# デフォルトでは開発用データベースに適用される.
$ bin/rails db:migrate
# 本番用データベース.
$ bin/rails db:migrate RAILS_ENV=production
# 開発用データベース.
$ bin/rails db:migrate RAILS_ENV=test

Railsでは現在のデータベース構造をdb/schema.rbに自動出力させる.

# 最新までマイグレーションを適用.
$ bin/rails db:migrate
# 特定verまでマイグレーションが適用された状態にする(ファイルの先頭の数字部分を指定).
$ bin/rails db:migrate VERSION=xxxxxx
# バージョンを1つ戻す.
$ bin/rails db:rollback
# 指定したステップ数だけバージョンを戻す.
$ bin/rails db:rollback STEP=2
# バージョンを1つ戻してから1つ上げる.
$ bin/rails db:migrate:redo

マイグレーションに失敗した場合、適用しかけた変更はロールバックされ、元の状態に戻される.

■ データ内容の制限.

Taskテーブルのname(名前)にNOT NULL制約を付与.

# 雛形ファイルの作成.
$ bin/rails g migration ChangeTasksNameNotNull
db/migrate/yyyymmddhhmmss_change_tasks_name_not_null.rb
class ChangeTasksNameNotNull < ActiveRecord::Migration[5.2]
  def change
    change_column_null :tasks, :name, false
  end
end

NOT NULL制約が付与されたかirbで確認.

$ bin/rails c

# nameがnilのTaskオブジェクトを作成し、DB登録を試してみる.
$ Task.new(name: nil).save

[ 実行結果 ]
ActiveRecord::NotNullViolation (PG::NotNullViolation: ERROR:  null value in column "name" violates not-null constraint)
DETAIL:  Failing row contains (6, null, null, 2019-12-15 13:13:22.582812, 2019-12-15 13:13:22.582812).
: INSERT INTO "tasks" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id"

Taskテーブルのname(名前)カラムを30文字以内に制限するマイグレーション実行.

$ bin/rails g migration ChangeTasksNameLimit30
db/migrate/yyyymmddhhmmss_change_tasks_name_limit30.rb
class ChangeTasksNameLimit30 < ActiveRecord::Migration[5.2]
  def up
    change_column :tasks, :name, :string, limit: 30
  end
  def down
    change_column :tasks, :name, :string
  end
end

Taskテーブルのname(名前)カラムをユニークに制限するマイグレーション実行.

$ bin/rails g migration AddNameIndexToTasks
db/migrate/yyyymmddhhmmss_add_name_index_to_tasks.rb
class AddNameIndexToTasks < ActiveRecord::Migration[5.2]
  def change
    add_index :tasks, :name, unique: true
  end
end

■ モデルの検証.

Railsにおけるモデル検証では「モデルオブジェクト(レコード)をデータベースに登録・更新する前に検証を行い、エラーがあれば登録・更新をしないで差し戻す」という仕組みとなっている.

# データベース登録・更新時に自動的に検証を行い、エラーがあればfalse返却.
task.save
# データベース登録・更新時に自動的に検証を行い、エラーがあれば例外生成.
task.save!
# validate: falseで検証スキップも可能.
task.save(validate: false)
# 検証処理を単体実行する場合に利用.
task.valid?

データの検証では以下3つの実装を追加.

:small_blue_diamond: Taskモデルのname属性に必須チェック付与.
:small_blue_diamond: Taskモデルのname属性に文字列長制限付与.
:small_blue_diamond: オリジナル検証コード実装.

① Taskモデルのname属性に必須チェック付与.

app/models/task.rb
class Task < ApplicationRecord
  validates: name, presence: true
end

# [ 実行結果 ]
# irb(main):001:0> task = Task.new
# => #<Task id: nil, name: nil, description: nil, created_at: nil, updated_at: nil>
# irb(main):002:0> task.save
#   (0.2ms)  BEGIN
#   (0.2ms)  ROLLBACK
# => false

# [ 実行結果 ]
# task.errors.full_messages
# => ["名称を入力してください"]

Taskモデルでの検証結果を元に、コントローラとビュー側に制御ロジックを追加.

app/controllers/task_controller.rb
def create
  @task = Task.new(task_params) # Taskモデルのnameに値が未設定の場合はfalse返却.
  if @task.save
    redirect_to @task, notice: "タスク「#{@task.name}」を登録しました。"
  else
    render :new # 検証エラーの場合は登録用フォームを再表示して再入力を促す.
  end
end
app/views/tasks/_form.html.slim
- if task.errors.present?
  ul#error_explanation
    - task.errors.full_messages.each do |message|
      li= message

② Taskモデルのname属性に文字列長制限付与.

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

③ オリジナル検証コード実装.

app/models/task.rb
# オリジナル検証コードの書き方は以下の2パターン.
# 1. 検証を行うメソッドを追加して、そのメソッドを検証用のメソッドとして指定.
# 2. 自前のValidatorを作って利用.
class Task < ApplicationRecord
  validate :validate_name_not_including_comma

  private

  # ぼっち演算子(&.):レシーバがnilであった場合でもエラーが発生しない.
  # object = nil
  # object.name  [実行結果] NoMethodError ※RailsがException生成.
  # object&.name [実行結果] nil
  def validate_name_not_including_comma
    # 「,(カンマ)」が含まれるかはinclude?を利用.
    errors.add(:name, 'にカンマを含めることはできません') if name&.include?(',')
  end
end

■ コールバック.

コールバックはモデル内で登録・更新など重要なイベント前後に任意の処理を挟む仕組みのこと.

今回はTaskモデルのname属性に値が未設定の場合は「名前なし」の値を自動的に設定.

app/models/task.rb
class Task < ApplicationRecord
  before_validation :set_nameless_name
...
  private 

  def set_nameless_name
    self.name = '名前なし' if name.blank?
  end
end

■ 参考文献.

: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
ユーザーは見つかりませんでした