Rails の ActiveRecord で1対多のテーブルを作る方法
例えば店舗情報を持つ、Shop モデルと、店舗の写真を持つ Photo モデルがあって、
Shop に対して、1対多で Photo モデルを関連付けしたい
開発環境
Rails 3.0.9
サンプルコード
class Shop < ActiveRecord::Base end class Photo < ActiveRecord::Base end
まず、所有先を格納するために、photoテーブルに、shop_id を追加する必要がある
所有する側の定義
has_many
class Shop < ActiveRecord::Base has_many :photos # 複数形であること end
所有される側の定義
belongs_to
class Photo < ActiveRecord::Base belongs_to :shop end
has_many で追加されるメソッドの例
shop = Shop.find(...) shop.each ... # 関連する情報については、配列と同じような制御ができる # それぞれ、実行のタイミングでDBに記録される shop.photos << photo # 写真追加 shop.delete(photo) # 写真関連取消(photoの、shop_id に NULL が入る) shop.clear # 写真全関連取消(photoの、shop_id に NULL が入る)
belongs_to で追加されるメソッドの例
photo = Photo.find(...) photo.shop # 店舗情報取得 photo.shop = shop # 店舗情報を格納 photo.save # save しないとDBに入らない
find の include オプション
shop = Shop.find(...).first
shop.photos.each
とかやると、each で、関連する情報にアクセスする毎に、クエリが走るのでとても非効率
find の時に、include オプションをつけると、関連するテーブルの情報もまとめて取得してくれるので、効率が良い
Shop.find(:all, :include => :photo, ...)
モデルを削除した時に、関連するモデルも削除したい場合
dependent オプションを使う
dependent オプションを付けない場合の挙動
photo = Photo.new(:name => 'photo_one') shop = Shop.new(:name => 'shop_one') photo.save; shop.save; shop.photos << photo shop.destroy
DBを見るとshopは削除されたが、photo は、shop_id に値があるままで残っている
sqlite> select * from photos; id shop_id name crea upda ---- ------------- ---- ---- ---- 2 4 photo_one 2012-04-03 08:06:12.897753 2012-04-03 08:15:46.275918
dependent オプションをつけた場合の挙動
class Shop < ActiveRecode::Base # destroy 実行時に、関連するモデルにも、destroyを実行させる has_meny :photos, :dependent => :destroy end
この場合、shop.destroy 時に、photo も削除される
destroy 以外に、delete オプションもあるけど、今のところ使わないので、解説したリンクだけ貼っとく