22
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Active Recordの基本的な操作メモ

Last updated at Posted at 2018-07-21

目的

sinatraからのDB利用時、最初によく使いそうなActiverecordの基本的な操作と知識をまとめる

詳細は公式ドキュメントで把握した方が早い

Raisl Guide

前提

  • Mac High Sierra
  • windowsでやる場合はvagrant上で
  • ruby 2.4.1 (rubyが入っていない場合は、下部参照を確認) (バージョンが変わらない場合は、下部参照を確認)
  • sqlite3 3.19.3

Sinatraの導入
https://qiita.com/yukihigasi/items/ffce19609ad8d935b7b0
sinatraでアプリを作る
https://qiita.com/yukihigasi/items/284418046b8aac55d05b

  • 続き、という訳ではないので、新規にプロジェクトを作って行います。

手順

手軽に試せる環境の準備

Activerecordの準備

bundle init
  • activerecordとsqlite3のgemを追加
Gemfile
source 'https://rubygems.org'
gem 'activerecord'
gem 'sqlite3'

  • bundle installでインストールします
  • bundle install

練習用インメモリDBの準備

  • sqlite3データベースと、スキーマを作ります
  • とりあえずテスト用として、seeds.sqlにDDLを書き、以下のようにdbファイルを作成します
$ sqlite3 db1.db < seeds.sql
  • テーブル
  • 単数のusersと多数のposts、という一対多構造のテーブルは以下のように書きます
  • 外部キーを、多側は {テーブル名}_id user_id integer のように持たせる必要があります
  • id をprimary key; 制約をつけると、明示的に指定しない場合、自動的にキーを付与してくれます
  • created_at、updated_at というカラムを設定すると、自動的にタイムスタンプを保持してくれます
  • 一応、サンプルとして、以下のようなDBとします
  • なんども初期化して実行を試すのであれば drop table if exists テーブル名;を ファイルの頭に設定して置いてください
seeds.sql
create table users (
  id integer primary key,
  name text,
  age integer,
  created_at,
  updated_at
);

create table posts (
  id integer primary key,
  user_id integer,
  message text,
  created_at,
  updated_at
);
  • activerecordの接続設定は以下のように行います
  • pp は、ActiveRecordのモデルクラスを、そのままではオブジェクトIDが出てしまうので、読みやすいように出力してくれます
  • loggerは実行されたSQLなどを出力してくれます
require 'active_record'
require 'pp'
require 'logger'

ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Base.default_timezone = :local

ActiveRecord::Base.establish_connection(
  adapter: 'sqlite3',
  database: './db1.db'
)
  • ここから、activerecordの操作の内容になりますが、
  • 特に手順、という訳ではなく、こうしたい時、こうする、というのを列記していきます

CLUD

  • 今回、Userというモデルが、id、name、age3つの項目を持っている場合を想定し例示します
class User < ActiveRecord::Base
end����������������������������������������������������������������������������������������

データの追加

  • 以下のようにcreateメソッドで、カラム名をシンボルで指定して作成できる
User.create(name: "testuser", age: 22)
  • オブジェクトを作り、値を代入する方法も可能
  • この場合は、saveメソッドを使い永続化する必要がある
user = User.new
user.name = 'testuser'
user.age = 27
user.save

データの表示


User.find_all
  • で全件の取得が可能
  • select句のように、取得する項目を選択する場合は、selectメソッドを先に使う
User.select("id, name").find_all
  • find(数値) で検索をすると、自動的に主キーで検索が行われる
  • 以下のようにfindメソッドは、項目を取ることもできる
User.find(3)
User.find_by_id(3)
User.find_by(name: "testuser")
User.find_by name: "testuser"
User.find_by_name("testuser")

参考 http://railsdoc.com/references/find

  • firstメソッド、lastメソッドで、最初のデータや最後のデータから取得することができる
  • 引数に値を持つことができ、その場合「何番目」のデータであるかを指定できる
User.first
User.last
User.first(3)
  • 存在しない場合
  • find_byメソッドに結果がない場合はnilが返される
  • find_byメソッドに!マークを付与した場合、検索なしはエラーとしてエラーが返却される
User.find_by_id 100
User.find_by_id! 100
  • where句による検索
  • カラム名のシンボルに値を渡すことで、絞り込みができる
  • 複数の値を与えるばあいは、配列を渡すことができる [21,22,23]
  • 範囲を与えることもできる21..23
User.where(age: 21)
  • AND条件
  • where条件は、複数組み合わせることで、AND条件として絞り込むことができる
  • 以上は以下のように"age <= 22" 定義することができる
User.where("age <= 22").where(name: "testuser")
  • OR条件で検索
  • whereメソッドは、orメソッドでor条件を追加することができる
  • この時、orメソッドの引数には、モデル.where、の構文が必要になる
User.where(name: "ni").or(User.where(age: 22)).select("id, name")
  • 以外(NOT)を検索する
  • whereメソッドは、notメソッドで、除外するデータを指定できる
User.select("id, name, age").where.not(name: "yaki")

プレースホルダ

  • User.where(name: "ni").or(User.where(age: 22))
  • 上のwhereは、プレースホルダを利用して、以下のように表記ができる
  • User.where("name == ?", {変数}).or(User.where("age == ?", {変数}))
  • SQLインジェクションを防ぐため、プレースホルダを利用するべき
  • また、プレースホルダは、シンボルで定義することも可能
User.where("name  == :name", {name: name}).or(User.where("age == :age", {age: age})).select("id, name")

データの編集

  • 第一引数に検索キーを指定し、カラム名のシンボルと値を指定して更新する
  • 以上を更新する、といった場合は "カラム名<=値" をwhereに引数で渡し、updateすることで実現できる
User.update(1, age: 30)
User.where("age <= 21").update(age: 23)
  • 特定の属性を変更する場合は、attributesメソッドを利用する
user.attributes = { name: "testuser3", age: 10 }

update_attribute とupdate_attribtues の違い
http://d.hatena.ne.jp/LukeSilvia/20080816/p2

データの削除

  • deleteメソッド、またはdestroyメソッドが利用できる
  • destroyメソッドの場合は、関連テーブルも含めて削除を行うことができる(ただしモデルに事前に定義が必要)
  • 引数には、条件を文字列で引数に取ることもできる
User.delete(1)
User.destroy_all("updated_at < '2004-04-04'")

使いそうな応用

ソート

  • orderメソッドを利用する
  • 与えたシンボルのカラムを軸にソートできる
  • シンボルで:desc を取ると降順にすることができる
  • また、.offsetメソッドで、最初のデータをいくつ飛ばすか指定することができる
  • limitを指定すると、何軒までを取得するかを定義することができる
User.order(:name)
User.order(name: :desc)
User.limit(20).order("updated_at DESC")

曖昧検索(部分一致)

  • where("カラム名 like '%検索テキスト%'") を指定し、部分一致を定義することができる
  • %の部分に任意の値が入っているデータも検索される
  • で終わる文字列を検索、といった場合は、カラム名 like '%ん'となる
User.where("name like ?", "%ん")

もしいなければ新規作成

  • find_or_create_byメソッドを利用すると、存在しなければ作成、という処理を行うことができる
user= User.find_or_create_by(name: "drip") do |u|
end

バリデーション

  • 空の文字が登録されたり、というようなことを防ぐため、バリデーションを追加することができる
  • また入力文字の最大長やフォーマットを制限することができる
  • Modelクラスで、以下のように定義する
  • validates(検証するフィールド名, :length => 検証パラメータ)
class User < ActiveRecord::Base
  # 必須条件の設定
  validates :name,:age, presence: true

Railsドキュメント validates
http://railsdoc.com/references/validates

共通化

  • 抽出条件を切り出す
  • スコープ化し、よく使うDB操作を共通化することができます
  • モデルに以下のように実装します
scope :select_top, -> (num){ User.order(:name).limit(num) }
  • scopeの次にシンボルでスコープ名を定義します。この場合はselect_top
  • 使う側からは、User.select_top 引数のように呼び出せます
  • ,以降は、(num)は仮引数です、次のブロックに定義された、User.order(:name).limit(num) を実行します。ブロック内は、通常のactiverecordによる検索処理の書き方と全く同じです。
  • このようによく使う処理を、scopeに切り出してくることができます

可変where句 

  • 複数の検索軸で and 検索
  • スペース区切りの文字列を分解して、繰り返しながらwhere条件を追加していく、という方法が取れるようです。
user = User.all
if {検索対象の文字列}.present?
    {検索対象の文字列}.split(" ").each do |n|
    user= User.where("name like :q", q: "%#{n}%")
  end 
end

ActiveRecordで動的なAND検索する
http://d.hatena.ne.jp/jiikko/touch/20130717/1374068054

1対多テーブル

  • Userモデルが、複数のPostを持つ、という想定でリレーションを作ってみます。

  • 設定

  • テーブルが、設定できている必要があります。小テーブルの方は、親テーブルの主キーを外部キーとして持ちます

  • 今回の例ではuser_id integer,が外部キーです

create table users (
  id integer primary key,
  name text,
  age integer,
  created_at,
  updated_at
);

create table posts (
  id integer primary key,
  user_id integer,
  messsage text,
  created_at,
  updated_at
);
  • モデル上では以下のように定義します
  • 親テーブルは、has_menyで子テーブルを定義します
  • dependentでdestroyを指定すると、子テーブルの変更が外部テーブルにも反映されるようになります。子テーブルを削除すると、外部テーブルも一緒に削除されます
  • 子テーブル側は、belongs_toで紐付くテーブルを指定します
class User < ActiveRecord::Base
  # 関連する小テーブルのデータも一緒に削除する
  has_many :posts, dependent: :destroy
end

class Posts < ActiveRecord::Base
  belongs_to :user
end
  • マイグレーションファイル上では、以下のように定義します
class CreateUser < ActiveRecord::Migration[5.0]
  def change
    create_table :user do |t|
      t.string :name
      t.timestamps
    end
 
    create_table :posts do |t|
      t.belongs_to :user, index: true
      t.string :message
      t.timestamps
    end
  end
end�������������������������������������������
  • 実際の使用
  • 挿入
  • userに紐付くPostオブジェクトを追加する
  • オブジェクトの名前は、多側の方は複数形の名称となっているので注意する
  • Postオブジェクトを作る時に、外部キーを指定して紐づける場合
user=User.find(1)
Post.create(user_id: 2, message: "testetst")
  • また、userオブジェクトに紐付くpostsオブジェクトのcreateを実行することで追加を行う方法もある
user.posts.create(message: "add text2")
  • 上記は、以下のように書くこともできる。
user.posts << Post.create(message: "add text <<")
  • 検索
  • 親モデルにincludesメソッドを利用し、関連テーブルも込で検索することができる
## userモデルと紐づくpostsを取得する
user = User.includes(:posts).find(1)
pp user
pp user.posts
  • 親テーブルに紐付くデータをループして取得する場合、以下のように書く
user.posts.each do |post|
  pp "#{user.name} : #{post.message}"
end
  • 逆に、以下のように、子テーブル側から親テーブルのカラムを取得することもできる
# 多側から取得する
post = Post.includes(:user).all
post.each do |p|
  # 多のデータを全てループ取得する
  # 親テーブルは、モデル名で呼び出すことができる
#  p "#{p.message} : #{p.user.name}"
end

# 親データが削除されると、modelにdependent destroy設定が定義されていれば、同時に小テーブルのデータも削除できる
User.find(1).destroy
  • 引き続き、便利だと思った操作、よく使う操作を加筆していきます。

その他

PostgreSQLの操作
## ローカルでpostgresqlサーバを準備 - 起動・停止・DB確認・ユーザ確認・ユーザー作成と権限付与のメモ
  • DBの確認
  • PostgreSQLの起動
postgres -D /usr/local/var/postgres
  • pg_ctlによる起動、停止は以下

  • 起動$ pg_ctl -D /usr/local/pgsql/data -l /var/log/postgres start

  • 停止pg_ctl -D /usr/local/pgsql/data stop

  • $psql -l でテーブルを確認できる

  • Databaseの準備

$ psql -q -c'select * from pg_user' postgres
 usename  | usesysid | usecreatedb | usesuper | userepl | usebypassrls |  passwd  | valuntil | useconfig
----------+----------+-------------+----------+---------+--------------+----------+----------+-----------
 katoyuki |       10 | t           | t        | t       | t            | ******** |          |
(1 row)

$ createuser sinatra_user
$ psql -q -c'select * from pg_user' postgres
   usename    | usesysid | usecreatedb | usesuper | userepl | usebypassrls |  passwd  | valuntil | useconfig
--------------+----------+-------------+----------+---------+--------------+----------+----------+-----------
  ...      |       10 | t           | t        | t       | t            | ******** |          |
 sinatra_user |    16384 | f           | f        | f       | f            | ******** |          |
(2 rows)

i$ createdb sindb -O sinatra_user
$ psql -l
                                    List of databases
   Name    |    Owner     | Encoding |   Collate   |    Ctype    |   Access privileges
-----------+--------------+----------+-------------+-------------+-----------------------
 postgres  | ...     | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
 sindb     | sinatra_user | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
 template0 |  ...      | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/ ...           +
           |              |          |             |             |  ... =CTc/ ... 
 template1 |  ...      | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/...          +
           |              |          |             |             |  ... =CTc/ ... 
(4 rows)

  • アプリ用ユーザsinatra_userにSuperuser権限を与えます
$psql -U {オーナーのユーザー} sindb
psql (10.4)
Type "help" for help.

sindb=# ALTER ROLE sinatra_user WITH Superuser;
ALTER ROLE
sindb=# \du
                                     List of roles
  Role name   |                         Attributes                         | Member of
--------------+------------------------------------------------------------+-----------
 ...     | Superuser, Create role, Create DB, Replication, Bypass RLS | {}
 sinatra_user | Superuser                                                  | {}
  • アプリ用のsinatra_userに権限が付与されました

参考

ActiveRecord入門
https://qiita.com/gcfuji/items/80625a75959591c2b7cd
Railsガイド
https://railsguides.jp/active_record_basics.html
Sinatra+ActiveRecord+MySQLで、簡単APIサーバ構築
https://qiita.com/u1_fukui/items/88c10d4d530ec6fbaaa1
ドットインストール
https://dotinstall.com/lessons/basic_activerecord_v2
ActiveRecord入門
https://qiita.com/gcfuji/items/80625a75959591c2b7cd
MacにPostgreSQLをインストール
https://qiita.com/_daisuke/items/13996621cf51f835494b
MacのRailsアプリでPostgreSQLを使う方法
https://qiita.com/yh2020/items/8be3087004d100fe752b
Herokuにデプロイできない非エンジニアです。
https://qiita.com/Taak15/items/8a7325c302d0d1b019f5

22
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?