30DAYSトライアル4th(Rails)DAY30(仮)の課題は「ドットインストール Active Record入門コース」です。
※(仮)としているのは東京フリーランスの記事ではDAY20までしか書かれていないので、その先を勝手に自分で決めて進めているからです。
30DAYSトライアル4th(Rails)DAY30(仮)の学習内容
Progate、ドットインストールのRailsコースにて少なからずActive Recordには触れているので、一気に全部いっちゃいます。
- #01 Active Recordを使ってみよう
- #02 データベースの準備をしていこう
- #03 Active Recordの設定をしていこう
- #04 レコードを挿入してみよう
- #05 Loggerを使ってSQLを確認しよう
- #06 レコードを抽出してみよう
- #07 抽出条件を指定してみよう
- #08 whereを使って抽出してみよう
- #09 プレースホルダーを使ってみよう
- #10 order 、limit、offsetを使ってみよう
- #11 抽出条件を登録してみよう
- #12 finde_or_create_byを使おう
- #13 データを更新してみよう
- #14 データを削除してみよう
- #15 validationを使ってみよう
- #16 Callbackを設定してみよう
- #17 Associationを設定してみよう
- #18 関連するデータを抽出しよう
- #19 関連するデータを削除しよう
#01 Active Recordを使ってみよう
Active RecordはRailsの一部として開発されたOR(O;オブジェクトとR:RDB)マッパー(マッピング)で、ざっくり言うとデータベース(RDB)のデータをオブジェクトとして扱えるようにするものになります。
これのおかげでRailsからSQLを意識することなくオブジェクトの操作のみでデータベースへのデータの取得・作成・更新・削除などができるようになります。
またRailsの一部として開発されているが、Active Record単体でも使用できる。
Active Recordのインストール
ドットインストールではRailsと同様にドットインストール用の仮想環境(ローカル開発環境の構築 [macOS編])を使用しますが、僕は自分のPC(Mac)に最新のバージョンをインストールしました。
Active Record単体をインストールする場合はターミナルにて下記コマンドを実行します。
# 通常実行
gem install activerecord
# スーパーユーザーで実行
sudo gem install activerecord
# 以下のような書き込み権限のエラーが発生したら、スーパーユーザー(sudo)でコマンドを実行する
ERROR: While executing gem ... (Gem::FilePermissionError)
You don't have write permissions for the /Library/Ruby/Gems/2.6.0 directory.
# 以下のようなメッセージが表示されれば、インストール完了
Successfully installed activerecord-6.0.1
Parsing documentation for activerecord-6.0.1
Done installing documentation for activerecord after 3 seconds
1 gem installed
SQLiteのインストール
学習で使用するデータベースのSQLiteもインストールします。
SQLiteもgemからインストールを行いますので、下記のコマンドをターミナルにて実行します。
# 通常実行
gem install sqlite3
# スーパーユーザーで実行
sudo gem install sqlite3
# 以下のような書き込み権限のエラーが発生する場合は、スーパーユーザーで実行する
Fetching sqlite3-1.4.1.gem
ERROR: While executing gem ... (Gem::FilePermissionError)
You don't have write permissions for the /Library/Ruby/Gems/2.6.0 directory.
# 以下のようなメッセージが表示されればインストールOK
Successfully installed sqlite3-1.4.1
Parsing documentation for sqlite3-1.4.1
Installing ri documentation for sqlite3-1.4.1
Done installing documentation for sqlite3 after 0 seconds
1 gem installed
#02 データベースの準備をしていこう
データベースの準備として、SQLIteにデータベースを作成し、その中にテーブルを作成します。
テーブル作成コマンド
テーブルを作成する構文は以下の通りです。
create table テーブル名 ( id integer primary key, カラム名 データ型, カラム名 データ型, ・ ・ ・ created_at, updated_at )
上記のように作成したファイルを下記のようなコマンドをターミナルから実行することで読み込む。
sqlite3 データベース名 < インポートファイル名
- テーブル名は小文字の複数形とする(Rails同様命名ルールが細かく決められている)
- primary keyのカラム名はid、データ型はintegerにする
(Active Recordが自動的に連番にしてくれる) - created_at、updated_atカラムを用意するとActive Recordが自動的に作成・更新日時を管理してくれる
SQLiteにデータを外部ファイルからインポートする
SQLiteへのデータを外部ファイルからインポートするには、下記のコマンドをターミナルにて実行します。
sqlite3 データベース名 < インポートファイル名
- SQLiteのデータベースは「データベース名」というファイル
#03 Active Recordの設定をしていこう
RubyにActive Recordの設定をしていきます。
基本的な設定はRubyのファイル(ファイル名.rb)に以下のような内容を記述していく。
# Active Recordの読み込み require 'active_record' # オブジェクトを分かりやすく表現してくれるpretty print require 'pp' # タイムゾーンの設定(東京) Time.zone_default = Time.find_zone! 'Tokyo' ActiveRecord::Base.default_timezone = :local # データベース接続設定(SQLiteの場合) ActiveRecord::Base.establish_connection( "adapter" => "sqlite3", "database" => "./データベース名" ) # テーブルとオブジェクトの紐つけ # オブジェクト名はテーブル名の単数形で頭文字を大文字とする class オブジェクト名 < ActiveRecord::Base # テーブル名が「users」の場合は「User」となる end
- SQLiteにはユーザー、パスワードは不要
- オブジェクト名は単数形の頭文字を大文字とする
#04 レコードを挿入してみよう
テーブルにレコードを挿入する方法になります。
オブジェクトを生成し、保存する
テーブルにレーコードを挿入する場合は、該当のオブジェクトを生成し、そのオブジェクトでデータベースに保存するというようなコードを記述します。
# オブジェクトの生成(どちらの記述も同じ意味) # 記述1 変数名 = オブジェクト名.new 変数名.カラム名1 = データ 変数名.カラム名2 = データ ・ ・ ・ # 記述方法2 変数名 = オブジェクト名.new(:カラム名1 => データ, :カラム名2 => データ, ・・・・) # 記述方法3 変数名 = オブジェクト名.new(カラム名1: データ, カラム名2: データ,・・・) # 記述方法4 変数名 = オブジェクト名.new |ブロック名| ブロック名.カラム名1 = データ ブロック名.カラム名2 = データ end # オブジェクトの保存 変数名.save
オブジェクトの生成、保存を同時に行う
オブジェクトの生成と保存を同時に行うこともできます。
オブジェクト名.create(カラム名1: データ, カラム名2: データ, ・・・)
#05 Loggerを使ってSQLを確認しよう
Loggerを使用することで、オブジェクトを保存する際などにどのようなSQLが発行されているかを確認することができます。
Loggerを使用する方法はRubyの設定を行ったファイルに以下を追記します。
require 'logger' # 標準出力(STDOUT)にログを出力する ActiveRecord::Base.logger = Logger.new(STDOUT)
#06 レコードを抽出してみよう
レコードの抽出(取得)方法は以下のように記述します。
# データ全件取得
User.all
# 取得カラムを指定して全件取得
User.select("カラム名1, カラム名2, ・・・).all
# 最初の1件を取得
User.all.first
User.select("カラム名1, カラム名2, ・・・).all.first
# 最後の1件を取得
User.all.first
User.select("カラム名1, カラム名2, ・・・).all.first
# 最初からn件を取得
User.all.first
User.select("カラム名1, カラム名2, ・・・).all.first(n)
pretty print(pp)
pretty printを読み込んで使用すると、テーブルデータの取得結果を分かりやすく表示することができます。
# pritty printの読み込み require 'pp' # pritty printを指定してデータ取得 pp オブジェクト名.all # 実行結果例 #<Memo:0x00007f82aa202270 id: 6, title: "t4", body: "b4", created_at: "2019-12-03 08:39:04.982337", updated_at: "2019-12-03 08:39:04.982337">]
#07 抽出条件を指定してみよう
抽出(取得)条件を指定して取得する方法です。
find
id指定でデータを取得します。
モデル名.ind(id値)
find_by
id以外のカラムの条件を指定してデータを取得します。
# 以下は全て同じ内容の記述 # 構文1 User.find_by(カラム名: 条件値) # 構文2「()」を省略 User.find_by カラム名: 条件値 # 構文3 User.find_by_カラム名(条件値) # 構文4「()」を省略 User.find_by_カラム名 条件値 # データが存在しなかた時にエラーとする場合(メソッド名の最後に「!:」をつける User.find_by!(カラム名: 条件値)
#08 whereを使って抽出してみよう
whereメソッドを使用するとより複雑なデータ検索が行える
モデル名.select("カラム名1, カラム名2, ・・・").where(カラム名: 20..29) # 範囲検索
モデル名.select("カラム名1, カラム名2, ・・・").where(カラム名): [19, 31]) # 複数値指定検索
AND検索
# どちらも同じ
User.select("カラム名1, カラム名2, ・・・").where("カラム名 >= 20").where("カラム名 < 30")
User.select("カラム名1, カラム名2, ・・・").where("カラム名 >= 20 and カラム名 < 30")
OR検索
# どれも同じ
User.select("カラム名1, カラム名2, ・・・").where("カラム名 <= 20 or カラム名 >= 30")
User.select("カラム名1, カラム名2, ・・・").where("カラム名 <= 20").or(User.select("カラム名1, カラム名2, ・・・").where("カラム名 >= 30"))
User.where("age <= 20").or(User.where("カラム名 >= 30")).select("カラム名1, カラム名2, ・・・")
NOT検索
User.select("カラム名1, カラム名2, ・・・").where.not(カラム名: 3)
#09 プレースホルダーを使ってみよう
whereの条件に変数を埋め込みたい場合は「#{}」で埋め込む方法は悪意のあるコードが入る可能性があるため、使用してはいけない。
かわりにプレースホルダーを使用して変数を埋め込む。
# 悪意のあるコードが入る可能性があるためNG
User.select("カラム名1, カラム名2, ・・・").where("カラム名 >= #{変数名1} and カラム名 < #{変数名2}")
# ?をつかう
User.select("カラム名1, カラム名2, ・・・").where("カラム名 >= ? and カラム名 < ?", min, max)
# シンボルを使う
User.select("カラム名1, カラム名2, ・・・").where("カラム名 >= :キー1 and カラム名 < :キー2", {キー1: 変数名1, キー2: 変数名2})
LIKE検索
User.select("カラム名1, カラム名2, ・・・").where("カラム名 like ?", "%i")
#10 order、limit、offsetを使ってみよう
並び替え(order)
# 昇順
モデル名.select("カラム名1, カラム名2, ・・・").order("カラム名")
モデル名.select("カラム名1, カラム名2, ・・・").order(:カラム名)
# 降順
モデル名.select("カラム名1, カラム名2, ・・・").order("カラム名 desc")
モデル名.select("カラム名1, カラム名2, ・・・").order(カラム名: :desc)
件数制限(limit)
User.select("カラム名1, カラム名2, ・・・").order(:カラム名).limit(3) # 先頭から3件
検索結果をとばす(offset)
User.select("カラム名1, カラム名2, ・・・").order(:カラム名).limit(3).offset(1) # 先頭から1件とばして3件
#11 抽出条件を登録してみよう
何度も使用するような抽出条件であれば抽出条件を登録して使用することができる。
class、method方式
該当モデルのmethodとして抽出条件を登録する。
class モデル名 < ActiveRecord::Base
def self.メソッド名(変数名1, 変数名2, ・・・)
select("カラム名1, カラム名2, ・・・").order(:カラム名.limit(変数名1)
end
end
# メソッドの呼び出し
モデル名.メソッド名
scope
該当モデルのscopeとして抽出条件を登録する
class モデル名 < ActiveRecord::Base
scope :スコープ名, ->(変数名1, 変数名2, ・・・) { select("カラム名1, カラム名2, ・・・").order(:カラム名).limit(変数名1) }
end
# スコープの呼び出し
モデル名.スコープ名
scopeの記述の場合は、その後にメソッドをつなげて記述できるので便利。
#12 find_or_create_byを使おう
「find_or_create_by」存在しない場合レコードを抽出条件に指定された場合、新しくレコードを作成できる。
# 基本構文 変数名 = User.find_or_create_by(カラム名: 条件値) # 条件に指定したカラム以外の値を埋めたい場合 変数名 = User.find_or_create_by(カラム名: 条件値) do |block| block.カラム名 = 値 ・ ・ ・ end
#13 データを更新してみよう
# update
# id指定の更新
モデル名.update(id値, 更新カラム名: 更新値)
# id以外のカラム指定の更新ん
モデル名.where(カラム名: 条件値).update(更新カラム名: 更新値)
モデル名.where(カラム名: 条件値).update(更新カラム名: 更新値, 更新カラム名: 更新値)
モデル名.where("カラム名 >= 条件値").update(更新カラム名: 更新値)
# update_all
モデル名.where("カラム名 >= 条件値").update_all("更新カラム名 = 更新値")
updateとupdate_allの使い分け
- バリデーション処理ができる
- 更新処理の前後に自動処理を追加できる
- 低速
- バリデーション処理ができない
- 更新処理の前後に自動処理を追加できない
- 高速
#14 データを削除してみよう
モデル名.delete(id値)
モデル名.where("カラム名 >= 条件値").delete_all
モデル名.destroy(id値)
モデル名.where("カラム名 >= 条件値").destroy_all
deleteとdestroyの使い分け
- バリデーション処理ができる
- 更新処理の前後に自動処理を追加できる
- 低速
- バリデーション処理ができない
- 更新処理の前後に自動処理を追加できない
- 高速
#15 Validationを使ってみよう
レコードを挿入したり更新したりする時のルール(チェック)をつけることができる。
class User < ActiveRecord::Base
# 必須
validates :カラム名, presence: true
# 必須のカラムを1行で複数で指定
validates :カラム名1, :カラム名2, ・・・ presence: true
# 3文字以下はNG
validates :カラム名, length: { minimum: 3 }
end
- validatesでNGになった場合はsaveなどを実行した結果がfalseとなる
(データ更新時のエラーハンドリングが行える)
#16 Callbackを設定してみよう
特定の処理が行われる前後で自動的になんらかの処理を行う仕組み。
# 例:削除の前後に処理を行う場合
class User < ActiveRecord::Base
before_destroy :前処理シンボル名
after_destroy :後処理シンボル名
protected
def 前処理シンボル名
処理
end
def 後処理シンボル名
処理
end
end
#17 Associationを設定してみよう
複数のテーブル(オブジェクト)を関連付けて扱うための仕組み。
# User1レコードに対して、複数のCommentを持つ場合 class User < ActiveRecord::Base # 複数のCommentをもつため、複数形 has_many :comments end class Comment < ActiveRecord::Base # 1つのuserとしか関連しないため、単数形 belongs_to user end
#18 関連するデータを抽出しよう
#17で関連付けたデータは「includes」を使用することで、同時に取得できる。
# User(1レコード)のテーブルを起点にデータ取得 user = User.includes(:comments).find(1) # 取得したデータを使用 pusts user.comments.name # Comment(複数レコード)のテーブルを起点にデータ取得 comments = Comment.includes(:user).all # 取得したデータを使用 comments.each do |comment| puts comment.user.name end
#19 関連するデータを削除しよう
関連するデータを削除する場合はクラスに「dependent: :destroy」を追加する。
class User < ActiveRecord::Base has_many :comments, dependent: :destroy end
- 「delete」や「destroy」を使用しても関連したデータは削除されない。
- 「has_many」側に定義する
まとめ
環境構築(Active RecordやSQLiteのインストール方法など)まで学習できるがドットインストールの良いところですね。
だいたいはProgateとドットインストールのRailsコースで出てきた内容になりますが、一度見ておいたら知識の幅が広がると思います。
あとはActive RecordというかRubyは色んなん書き方ができるのでややこしいです。
ドットインストールの動画では全て読めるようにしましょうと言われており、そりゃそうだとは思いますが、どの書き方が主流とか、メリット、デメリットはもう少し教えてほしいところです。
DAY21以降は勝手に決めて進めましたが、一応これで30DAYSトライアル4th(Rails)は終了になります。
これからはRailsチュートリアルを進めていきたいと思います。


コメント