そういうことだったんですね

いろいろ調べたり学んだりしたことを忘れないように書き連ねています

rails - CRUDのまとめ



新規作成

レコードを新規作成する。

create
Entry.create title: "タイトル", body: "本文"
INSERT INTO "entries" ("body", "created_at", "title", "updated_at") VALUES (?, ?, ?, ?)  [["body", "本文"], ["created_at", Wed, 05 Mar 2014 19:40:52 UTC +00:00], ["title", "タイトル"], ["updated_at", Wed, 05 Mar 2014 19:40:52 UTC +00:00]]
new & save

以下のように、インスタンスを作成後保存する方法でも可能

@entry = Entry.new title: "タイトル", body: "本文"
@entry.save
INSERT INTO "entries" ("body", "created_at", "title", "updated_at") VALUES (?, ?, ?, ?)  [["body", "本文"], ["created_at", Wed, 05 Mar 2014 19:41:27 UTC +00:00], ["title", "タイトル"], ["updated_at", Wed, 05 Mar 2014 19:41:27 UTC +00:00]]

更新

特定のレコードを更新する

update

クラスメソッド

Entry.update 7, title: "別のタイトル"
UPDATE "entries" SET "title" = ?, "updated_at" = ? WHERE "entries"."id" = 7  [["title", "別のタイトルです"], ["updated_at", Wed, 05 Mar 2014 19:25:53 UTC +00:00]]

インスタンスメソッド

@entry = Entry.find 7
@entry.update title: "別のタイトル"
UPDATE "entries" SET "title" = ?, "updated_at" = ? WHERE "entries"."id" = 7  [["title", "別のタイトル"], ["updated_at", Wed, 05 Mar 2014 19:24:58 UTC +00:00]]
find & update

レコードの属性を作成して更新してもよい

@entry = Entry.find 1
@entry.title = "新しいタイトル"
@entry.save
UPDATE "entries" SET "title" = ?, "updated_at" = ? WHERE "entries"."id" = 11  [["title", "新しいタイトル"], ["updated_at", Wed, 05 Mar 2014 19:47:20 UTC +00:00]]
new & update

まだ保存されていないオブジェクトの場合は、新規作成される

@entry = Entry.new
@entry.update title: "タイトル3"
INSERT INTO "entries" ("created_at", "title", "updated_at") VALUES (?, ?, ?)  [["created_at", Wed, 05 Mar 2014 19:29:09 UTC +00:00], ["title", "タイトル3"], ["updated_at", Wed, 05 Mar 2014 19:29:09 UTC +00:00]]

削除(destroy,delete)

destroy

before_destroy コールバックが呼び出される。
トランザクション内で実行される。
削除したオブジェクトを返す。
オブジェクトはfreezeされるため、再度保存することはできない。
(destroyed? メソッドで確認可能)

クラスメソッド

Entry.destroy 8
DELETE FROM "entries" WHERE "entries"."id" = ?  [["id", 8]]

全レコードを1レコードずつ削除する。
1レコードずつトランザクション内で実行される。

Entry.destroy_all
DELETE FROM "entries" WHERE "entries"."id" = ?  [["id", 9]]
DELETE FROM "entries" WHERE "entries"."id" = ?  [["id", 10]]

インスタンスメソッド

@entry = Entry.find 1
@entry.destroy
@entry.destroyed?  #=> true
DELETE FROM "entries" WHERE "entries"."id" = ?  [["id", 12]]
delete

レコードの削除。
コールバックは呼び出されない。

クラスメソッド

削除したレコード数を返す。

Entry.delete 1
DELETE FROM "entries" WHERE "entries"."id" = 11

全レコードを削除する。

Entry.delete_all
DELETE FROM "entries"

インスタンスメソッド

削除したインスタンスを返す。freezeされるので新規保存は不可能。

@entry = Entfy.find 7
@entry.delete
@entry.destroyed?  #=> true
DELETE FROM "entries" WHERE "entries"."id" = 7


Rubyレシピブック 第3版 303の技

Rubyレシピブック 第3版 303の技

rails - クエリの基本(2)



特定のフィールドを取得する

select を使う

User.select(:name)
SELECT name FROM "users"

複数のフィールド名を指定できる

User.select(:id, :name)
SELECT id, name FROM "users"

フィールド名は,区切りの文字列でもよい。
そのまま渡されるので max や minなども使える(てしまう)。

User.select("id,name")
SELECT id,name FROM "users"
ソートする

order を使う。

User.order(:created_at)
レコードの取得数と取得開始位置を制御する

limit と offset を使う。

主キー(id)のソートは無くなるので、orderで明示しないと
結果が異なる。

次の例は先頭5件目から30件ずつ表示する。

User.limit(30).offset(5)
SELECT "users".* FROM "users" LIMIT 30 OFFSET 5

なお、limit と offset は逆にしても同じ結果が得られる。

User.offset(5).limit(30)
グループ化する

group と having を使う。

必要な部分や集計結果を取得する用途には select とあわせて
使うことが多いだろう。

結果は呼び出し元のモデルクラスのインスタンスとして返される。

result = User.select("sex,count(sex) as sex_count").group(:sex).having("age < 20").order(:sex)
result[0]   # Userのインスタンス
result[0].sex   # 1
result[0].sex_count   # 4
SELECT sex,count(sex) as sex_count FROM "users" GROUP BY sex HAVING age < 20 ORDER BY "users"."sex" ASC


パーフェクトRuby (PERFECT SERIES 6)

パーフェクトRuby (PERFECT SERIES 6)

rails - クエリの基本

クエリの流れ

  • オプションからSQL文を組み立てる
  • SQLを発行し、データベースからデータを取得する
  • 取得したデータから Rubyオブジェクトを生成する
  • after_find コールバックを呼び出す

クエリを行うメソッド

Model.take(limit)

レコードに格納されている最初の値を取得する。
limit省略時は 1。

Blog.take
SELECT "blogs".* FROM "blogs" LIMIT 1
Model.first(limit)

主キーでソートした最初の値を取得する
limit省略時は 1。

Blog.first
SELECT "blogs".* FROM "blogs" ORDER BY "blogs"."id" ASC LIMIT 1
Model.last(limit)

主キーでソートした最後の値を取得する
limit省略時は 1。

Blog.last
SELECT "blogs".* FROM "blogs" ORDER BY "blogs"."id" DESC LIMIT 1
Model.find

引数に primary_key (デフォルトは id) を指定する

Blog.find 5
SELECT "blogs".* FROM "blogs" WHERE "blogs"."id" = ? LIMIT 1  [["id", 5]]

キーに配列を指定すると複数のデータを取得できる

Blog.find [5,6]
SELECT "blogs".* FROM "blogs" WHERE "blogs"."id" IN (5, 6)
Model.find_by

引数に 検索条件を属性名:値 を渡すと、合致したレコードを取得できる

Blog.find_by user_id: 3
SELECT "blogs".* FROM "blogs" WHERE "blogs"."user_id" = 3 LIMIT 1

引数を複数渡すと、AND条件で結んだ条件と合致したレコードを取得できる

Blog.find_by user_id: 3, title: "タイトル"
SELECT "blogs".* FROM "blogs" WHERE "blogs"."user_id" = 3 AND "blogs"."title" = 'タイトル' LIMIT 1

複雑な条件を指定する

where を使う。

条件を直接SQLで指定する

SQLのwhere句を直接渡すことができる。
SQLインジェクション防止のため、変数を渡す際はプレースホルダを使うこと。

Blog.where "user_id = ?", params[:user_id]
SELECT "blogs".* FROM "blogs" WHERE (user_id = 3)

params[:user_id] = "1 OR 1" の場合。

プレースホルダを使わないと、

Blog.where "user_id = #{params[:user_id]}"
SELECT "blogs".* FROM "blogs" WHERE (user_id = 1 OR 1)

のように、全レコードを取得可能になってしまう。
プレースホルダを使うと、

Blog.where "user_id = ?", params[:user_id]
SELECT "blogs".* FROM "blogs" WHERE (user_id = '1 OR 1')

のように、適切にクオートされるので安全。

複数の条件を使う(1)

複数条件する場合は、プレースホルダの順番に値を渡す

Blog.where "user_id = ? AND created_at >= ?", params[:user_id], params[:created_at]
SELECT "blogs".* FROM "blogs" WHERE (user_id = 3 AND created_at >= '2014-02-17 19:15:35.523440')
複数の条件を使う(2)

? のかわりに Keyを指定し、値を Key/Value 型のHash を渡してもよい。

Blog.where "user_id = :user_id AND created_at >= :created_at", { user_id: params[:user_id], created_at: params[:created_at]}
SELECT "blogs".* FROM "blogs" WHERE (user_id = 3 AND created_at >= '2014-02-17 19:15:35.523440')