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

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

rails - ActiveRecord の コールバック

基本

  • オブジェクトの生成・更新・削除のタイミングで呼び出されるメソッド
  • トリガとなるイベント(validation,saveなど)の前(before)と後(after)に登録可能
  • コールバックはメソッドチェーンに追加される。
  • before_xxx で false を返すか例外を発生させると、それ以降の処理を停止させることができる (トランザクション中の場合は ROLLBACK を発生させる)

使い方

  • before_xxxx/after_xxxx メソッドでコールバックとするメソッド名を登録
  • メソッドを定義する

class Blog < ActiveRecord::Base
  after_save :saved_title

  protected
  after_validation :saved_title
    puts "Saved!!"
  end
end

利用可能なコールバックの種類と実行順序

Create
  • before_validation
  • after_validation
  • before_save
  • around_save
  • before_create
  • around_create
  • after_create
  • after_save
Update
  • before_validation
  • after_validation
  • before_save
  • around_save
  • before_update
  • around_update
  • after_update
  • after_save
Destroy
  • before_destroy
  • around_destroy
  • after_destroy
Find
  • after_initialize
  • after_find

条件付きのコールバック

:if/:unless を使うことで特定の条件のときのみコールバックを
実行させる/させないことができる

class SomeModel < ActiveRecord::Base
  before_save :some_action, if: :action_condition?

  protected
  def action_condition?
  end
end

:if/:unless には、シンボル、文字列、手続きオブジェクトが指定できる

モデルから再利用する

クラスにコールバック名と同名のメソッドを定義し、コールバックメソッド
にオブジェクトを渡す。

class SomeCallback
  def before_save
    # ...
  end
end

class SomeModel < ActiveRecord::Base
  before_save SomeCallback.new
end

rails - has_many through:

他対他 の関連を参照する

元々は他対他を直接参照できるようにするもの。

+-------+       +-----------+       +-----+
|Product| 1---n |OrderDetail| n---1 |Order|
+-------+       +-----------+       +-----+

例えば、特定の Order中の Productを調べたい場合など。

class Product < ActiveRecord::Base
  has_many :order_details
  has_many :orders, through: :order_details
end

class Order < ActiveRecord::Base
  has_many :order_details
  has_many :products, through: :order_details
end

class OrderDetail < ActiveRecord::Base
  belongs_to :product
  belongs_to :order
end

こうすると、

@orders = @product.orders

として参照できる

カスケードした関連を直接参照する
+----+       +----+       +-----+
|User| 1---n |Blog| 1---n |Entry|
+----+       +----+       +-----+

User から Entry のアイテムを直接参照できるようにする。

class User < ActiveRecord::Base
  has_many :blogs
  has_many :entries, through: :blogs
end

class Blog < ActiveRecord::Base
  has_many :entries
  belongs_to :user
end

class Entry < ActiveRecord::Base
  belongs_to :blog
end

次のように参照できる。

@user.entries

ただし、次のように作成することはできない。

@user.entries.create title: "ほげ"

1対多のカスケードとなっているため Blog が特定できない。
Blog の id を指定し追加する。

@blog = Blog.find @blog_id
@blog.entries.create title: "ほげ"

rails - ActiveRecord Migration のまとめ

基本

  • db/migration 以下に YYYYMMDDhhmmdd_XXX_YYY.rb のファイルを作成する
  • Migrationのファイル名とクラス名は一致していないとダメ
    • ファイル名: YYYYMMDDhhmmdd_create_users.rb
    • クラス名: CreateUsers
  • 自分で設定ファイルを作成可能だが 基本的には rails generate migrationコマンドを使用する。
  • DBに反映するには、rake db:migrate を実行する
    • reversible なコマンド(create_table/add_columnなど)については追加不要
    • カラム名の変更などは手動で定義する必要がある

CreateXXX - テーブルを作成する

$ rails generate migration CreateUsers

自動的に create_table が追加される。
テーブル名は複数形になる。

class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
    end
  end
end

同時にカラムの定義も可能。
引数に カラム名:型[:オプション(省略可)] (複数可) を指定する。
型省略時は string になる。

$ rails generate migration CreateUsers name:string email:string
class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :name
      t.string :email
    end
  end
end

AddYYYToXXX - カラムの追加する

カラムの追加。To以下にテーブル名を指定する。

$ rails generate migration AddNameAndEmailToUsers name:string email:string

db/migrate/20140218112618_add_name_and_email_to_users.rb

class AddNameAndEmailToUsers < ActiveRecord::Migration
  def change
    add_column :users, :name, :string
    add_column :users, :email, :string
  end
end

RemoveYYYToXXX - カラムを削除する

カラムの削除。追加時と同じく To以下にテーブル名を指定する。

$ rails generate migration RemoveEmailToUsers email:string

db/migrate/20140218112720_remove_email_to_users.rb

class RemoveEmailToUsers < ActiveRecord::Migration
  def change
    remove_column :users, :email, :string
  end
end

AddYYYRefToXXX - 外部テーブルへの参照を作成する

名前が user_id のカラムが追加され、インデックスが貼られる。
外部キー制約は設定されない(DBではなくモデル側でチェックを
するポリシーのため)。

$ rails generate migration AddUserRefToBlogs user:references

db/migrate/20140218114718_add_user_ref_to_blogs.rb

class AddUserRefToBlogs < ActiveRecord::Migration
  def change
    add_reference :blogs, :user, index: true
  end
end

migration の状態を調べる

$ rake db:migrate:status
database: /home/babiy/work/testapp/db/development.sqlite3

 Status   Migration ID    Migration Name
--------------------------------------------------
   up     20140218082310  Create users
   up     20140218082651  Add name and email to users
||<