Help us understand the problem. What is going on with this article?

ActiveRecord入門

More than 1 year has passed since last update.

概要

Railsの復習に伴って、ActiveRecordの最低限の知識を整理しました。

ActiveRecordとは

  • Ruby on Rails
    • ORマッパーにActiveRecordを採用している。
    • ActiveRecordはRuby on Railsが開発している。
  • OR Mapper (Object <> RDB)
    • SQLを意識せずにデータベースを扱う事が可能。
    • DBエンジンは原則なんでもOK。
  • Railsを利用せずとも、単体でも利用可能

命名規則

  • 非常に厳格
  • テーブル名は「小文字 + 複数形」
  • created_atとupdated_atで作成日時の更新日時を自動保存できる。

ActiveRecordの利用の流れ

  • ① DBにテーブルを作成
  • ② Ruby上でactive_recordをrequire
  • ③ Timezoneを日本に設定
  • ④ ActiveRecord::BaseにDB接続情報を記載
  • ⑤ テーブル名に基づくクラスを作成
  • ⑥ 上記のクラスからインスタンスを作成
    • インスタンスが1レコードになる

ActiveRecordの基本文法

INSERT(CREATE)

  • インスタンス化
    • レコードを作成準備
  • .save
    • レコードの保存
  • .create
    • レコードの作成と保存を同時に行う
insert.rb
require 'active_record'
require 'pp'
require 'logger'

# Timezoneの設定
Time.zone_default = Time.find_zone! 'Tokyo'
ActiveRecord::Base.default_timezone = :local
# ロガーの設定
ActiveRecord::Base.logger = Logger.new(STDOUT)
# DB情報
ActiveRecord::Base.establish_connection(
  "adapter" => "sqlite3",
  "database" => "./myapp.db"
)
# Modelを定義
class User < ActiveRecord::Base
end
# Modelのインスタンスが1レコードとなる
user = User.new(
  name: 'fujimoto',
  age: 27,
)
user.save

user = User.new
user.name = 'fujimoto'
user.age = 27
user.save

User.create(
name: 'fujimoto',
age: 27,
)

SELECT(READ)

特徴

  • 検索条件に引っかからなければnilが返される。
  • エラーで返したい場合は、メソッドに!をつける。 ### メソッド
  • .all
    • 全件取得
  • .select(‘, , …’).all
    • カラムを絞って取得
  • .first
    • 最初のレコードを取得
    • 引数を渡すと「最初の○件」の様に取得も可能
  • .last
    • 最後のレコードを取得
    • 引数を渡すと「最後の○件」の様に取得も可能
select1.rb
# 全件取得
pp User.all
# カラムを絞って全件取得
pp User.select('id, name, age').all
# 最初の3件を取得
pp User.select('id, name, age').first(3)
# 最後の2件を取得
pp User.select('id, name, age').last(2)
  • .find()
    • idで条件取得
    • データの取得は1件のみ
  • .find_by(: )
    • カラム名で検索
    • .find_by_()も可
    • データの取得は1件のみ
  • .where(: )
    • 返り値は必ず配列
  • .where(‘<条件>’)
    • SQLの条件ぽく条件を書くことも可能
  • .where(‘<条件>’).where(‘<条件>’)
    • AND検索
  • .where(‘<条件>’).or(.where(‘<条件>’))
    • OR検索
  • .where.not(‘条件’)
    • NOT検索
select2.rb
# idで条件を指定して検索
pp User.select('id, name, age').find(2)
# 特定のカラムで条件を指定して検索(3パターン)
pp User.select('id, name, age').find_by(name: 'maeda')
pp User.select('id, name, age').find_by name: 'horio'
pp User.select('id, name, age').find_by_name! 'urano'
# AND検索
pp User.select('id, name, age')
  .where('age >= 25')
  .where('age < 30')
# OR検索
pp User.where(age: 27)
  .or(User.where(age: 33))
  .select('id, name, age')
# NOT検索
pp User.where.not(id: 3).select('id, name, age')

DELETE(DELETE)

  • .destroy
    • レコードを削除する。
    • 低速
    • Validationなどの追加機能が考慮される
    • UPDATEのupdate
  • .delete
    • 単純にレコードを削除する。
    • 高速
    • Validationなどの追加機能は考慮されない
    • UPDATEのupdate_all
  • .delete_all
    • deleteの全件削除バージョン
destroy.rb
# destroyでidを指定して削除
User
  .destroy(3)
# 条件を指定して、条件に合致するレコードを全件削除
User
  .where('name like ?', '%a%')
  .delete_all

users = User.select('id, name, age').all
pp users

UPDATE(UPDATE)

  • .update(, )
    • idを指定してカラムを更新する
    • id以外の条件でレコードを指定する場合はwhereを利用する
    • ValidationなどActiveRecordの追加機能を利用できるが低速。
  • .update_all(‘更新内容’)
    • SQLのUPDATEっぽくupdateできる。
    • ValidationなどActiveRecordの追加機能は利用できないが、高速。
update.rb
# idを指定してレコードを更新
User.update(4, age: 26)
# 条件を指定してレコードを更新
User.where('age = 26').update(age: 27)
# SQLのUPDATEっぽくレコードを更新
User.update_all('age = 26')

users = User.select('id, name, age').all
pp users

Logger

  • Rubyの標準ライブラリ
  • requireして利用する
  • ActiveRecord::Base.loggerに出力場所を代入するとActiveRecordで発行されたSQLを確認する事ができる。

プレースホルダー

  • 変数に値を保持して、条件に当てる場合、直接変数を展開してはいけない。
    • 悪意のあるコードが代入される可能性がある為。
  • 条件に変数を利用したい場合は、プレースホルダーを利用する。
placeholder.rb
min = 25
max = 35

age = {
  min: min,
  max: max,
}
# プレースホルダーに?を利用した場合
pp User.select('id, name, age')
  .where('age >= ? and age < ?', min, max)
# プレースホルダーにシンボルを利用した場合
pp User.select('id, name, age')
  .where('age >= :min and age < :max', age)
# like検索でもプレースホルダーを利用する
pp User.select('id, name, age')
  .where('name like ?', '%f%')

並び順

  • order
    • カラムを元に並び替えを行う。
order.rb
# 単純に年齢で並び替える
users = User.select('id, name, age')
  .order(:age)
# 逆順で年齢で並び替える
users = User.select('id, name, age')
  .order(age: :desc)
# 最初の1件を飛ばして、3件若い順に年齢を取得する
users = User.select('id, name, age')
  .order(:age)
  .limit(3)
  .offset(1)

pp users
  • limit
    • 取得数を制限して取得する。
  • offset
    • 何番目から取得するかを指定して取得する。

条件の登録

  • 条件の登録はActiveRecord::Baseクラスを継承したModel内にクラスメソッドを定義するか、scopeを定義する事で実現する。
limit.rb
# 条件の登録
class User < ActiveRecord::Base
    # クラスメソッドVer.
  def self.top(num)
    select('id, name, age')
      .order(:age)
      .limit(num)
  end
    # scopeVer.
  scope :top, -> (num) {
    select('id, name, age')
      .order(:age)
      .limit(num)
  }
end

pp User.top(1)

レコードが見つからない場合のみ挿入

  • メールアドレスを検索してなければ登録的な処理で使える。
  • find_or_create_by
    • 条件検索に一致しない場合のみ登録し、条件に合致する内容があれば取得する。
    • 条件に一致する内容がない場合に、他のカラムも埋めたい場合は、ブロックを利用する。
find_or_create_by.rb
# find_or_create_by
## 条件に一致する内容がない場合は、ブロックも実行する。
user = User.select('id, name, age')
  .find_or_create_by(name: 'nu') do |u|
    u.age = 33
end

pp user

Validation

  • レコード挿入前にデータのチェックを行う事ができる
  • nameとageが必須で、nameは3文字以上のバリデーション
validation.rb
# nameとageがnilのデータを挿入
user = User.new(
  name: nil,
  age: nil,
)
# データの保存が成功すればok、失敗すればng\\\
if user.save
  p 'ok'
else
  pp user.errors.messages
end
# データの保存が失敗すればエラーを出す
# user.save!

users = User.select('id, name, age').all
pp users

Callback

  • 特定の処理が行われる前後に自動的に処理を入れる。
  • 各種CRUD系にbefore_, after_を付与する。
  • モデル内に記載する。
callback.rb
class User < ActiveRecord::Base
  before_destroy :print_before_msg
  after_destroy :print_after_msg

  protected
    def print_before_msg
      puts "#{self.name} will be deleted"
    end
    def print_after_msg
      puts "#{self.name} deleted"
    end
end


User.where('age >= 20').destroy_all

pp User.select('id, name, age').all

Association

  • テーブル同士を関連付けて、簡単にデータ抽出できる。
association.rb
require 'active_record'
require 'pp'
require 'logger'

Time.zone_default = Time.find_zone! 'Tokyo'
ActiveRecord::Base.default_timezone = :local
ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Base.establish_connection(
  "adapter" => "sqlite3",
  "database" => "./myapp.db"
)

class User < ActiveRecord::Base
  # 複数のcommentsレコードを持つ(複数系)
  has_many :comments, dependent: :destroy
end

class Comment < ActiveRecord::Base
  # 1つのuserに所属する(単数系)
  belongs_to :user
end

User.delete_all

User.create(name: 'user1', age: 27)
User.create(name: 'user2', age: 27)
User.create(name: 'user3', age: 33)
User.create(name: 'user4', age: 20)

Comment.delete_all

Comment.create(body: 'comment1', user_id: 1)
Comment.create(body: 'comment2', user_id: 2)
Comment.create(body: 'comment3', user_id: 1)

# includes(:symbol)でAssociationデータも引き取れる
# Modelでhas_manyで定義した名称
user = User.includes(:comments).find(1)

pp user.comments.each do |c|
  puts "#{user.name}: #{c.body}"
end

# includes(:symbol)でAssociationデータも引き取れる
# Modelでbelogs_toで定義した名称
comments = Comment.includes(:user).all

comments.each do |c|
  # c.user.nameでAssociationのUserデータを取得できる
  puts "#{c.body} by #{c.user.name}"
end

# IDが1のUserを削除(Commentを削除)
User.find(1).destroy

import.sql
drop table if exists users;
create table users (
  id integer primary key,
  name text,
  age integer,
  created_at,
  updated_at
);

drop table if exists comments;
create table comments (
  id integer primary key,
  user_id integer,
  body text,
  created_at,
  updated_at
);

最後に

ActiveRecordは自分の中ではかなり分かりやすいO/Rマッパーだと思います。
(柔軟だし、個人的には命名規則がしっかりしている方が好き)

Railsとの相性も抜群なので、理解を深めて柔軟に使いこなしていきたいと思います。

tfrcm
React / ReactNative / Go / TypeScript / AWS / Docker / k8s
https://gemcook.com
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
ユーザーは見つかりませんでした