LoginSignup
1
1

More than 5 years have passed since last update.

has_many 関連で関連元をセーブすると関連までセーブされる

Last updated at Posted at 2015-10-06

当たり前かもしれないけど、これでハマったので自分への教訓として

この記事の内容

  1. has_many 関連で、 1側 のモデルに紐ついた 多側new した後に、1側save! した時の挙動

結論

model.assertions.new した後に modelsaveするときはassertions もまとめて保存されるから気をつけよう。(便利だけど)

環境

適当な関連

スクリーンショット 2015-10-06 18.10.59.png

モデルのコード達

app/models/*.rb
class Post < ActiveRecord::Base
  belongs_to :user
end

class User < ActiveRecord::Base
  has_many :posts, dependent: :destroy
end

やっていくぞい

has_many 関連で、 1側 のモデルに紐ついた 多側new した後に、1側save! した時の挙動

まずは準備.rb
User.create(name: 'haito')
   (0.1ms)  begin transaction
  SQL (0.8ms)  INSERT INTO "users" ("created_at", "name", "updated_at") VALUES (?, ?, ?)  [["created_at", "2015-10-06 07:03:05.166165"], ["name", "haito"], ["updated_at", "2015-10-06 07:03:05.166165"]]
   (0.8ms)  commit transaction
=> #<User id: 1, name: "haito", created_at: "2015-10-06 07:03:05", updated_at: "2015-10-06 07:03:05">

user = User.first
  User Load (0.2ms)  SELECT  "users".* FROM "users"   ORDER BY "users"."id" ASC LIMIT 1
=> #<User id: 1, name: "haito", created_at: "2015-10-06 07:03:05", updated_at: "2015-10-06 07:03:05">
関連を確認.rb
user.posts
  Post Load (0.3ms)  SELECT "posts".* FROM "posts"  WHERE "posts"."user_id" = ?  [["user_id", 1]]
=> []

:ok_woman:

関連に対して(new|build)を実行.rb
user.posts.new(text: '万策尽きた〜')
=> #<Post id: nil, text: "万策尽きた〜", user_id: 1, created_at: nil, updated_at: nil>

user.posts
=> [#<Post id: nil, text: "万策尽きた〜", user_id: 1, created_at: nil, updated_at: nil>]

user.posts.new(text: 'どんどんどーなっつどーんといこー!')
=> #<Post id: nil, text: "どんどんどーなっつどーんといこー!", user_id: 1, created_at: nil, updated_at: nil>

user.posts
=> [#<Post id: nil, text: "万策尽きた〜", user_id: 1, created_at: nil, updated_at: nil>, #<Post id: nil, text: "どんどんどーなっつどーんといこー!", user_id: 1, created_at: nil, updated_at: nil>]

この時点で user インスタンスに対して Post モデルのインスタンスが紐付いているのが分かる。

ちょっと確認.rb
Post.all
  Post Load (0.1ms)  SELECT "posts".* FROM "posts"
=> []

しかし、まだ Post モデルはまだ一つもDBに保存されていない

では保存.rb
user.save!
   (0.1ms)  begin transaction
  SQL (0.3ms)  INSERT INTO "posts" ("created_at", "text", "updated_at", "user_id") VALUES (?, ?, ?, ?)  [["created_at", "2015-10-06 07:08:44.255076"], ["text", "万策尽きた〜"], ["updated_at", "2015-10-06 07:08:44.255076"], ["user_id", 1]]
  SQL (0.1ms)  INSERT INTO "posts" ("created_at", "text", "updated_at", "user_id") VALUES (?, ?, ?, ?)  [["created_at", "2015-10-06 07:08:44.257063"], ["text", "どんどんどーなっつどーんといこー!"], ["updated_at", "2015-10-06 07:08:44.257063"], ["user_id", 1]]
   (1.5ms)  commit transaction
=> true

Post.all
  Post Load (0.2ms)  SELECT "posts".* FROM "posts"
=> [#<Post id: 1, text: "万策尽きた〜", user_id: 1, created_at: "2015-10-06 07:08:44", updated_at: "2015-10-06 07:08:44">,
 #<Post id: 2, text: "どんどんどーなっつどーんといこー!", user_id: 1, created_at: "2015-10-06 07:08:44", updated_at: "2015-10-06 07:08:44">]

お〜 保存されましたね。1側User モデルへのクエリは発行されていませんが、Postへのクエリはガンガン発行されるようです :eyes:

ではちょっと別の視点から作ってみます。

Postモデルをnewしてから作成.rb
user = User.first
=> "省略"

post = Post.new(text: '上手くいかないことを人のせいにしているようなヤツは辞めちまえよ!')
=> #<Post id: nil, text: "上手くいかないことを人のせいにしているようなヤツは辞めちまえよ!", user_id: nil, created_at: nil, updated_at: nil>

post.save!
   (0.1ms)  begin transaction
  SQL (0.4ms)  INSERT INTO "posts" ("created_at", "text", "updated_at") VALUES (?, ?, ?)  [["created_at", "2015-10-06 07:14:06.612594"], ["text", "上手くいかないことを人のせいにしているようなヤツは辞めちまえよ!"], ["updated_at", "2015-10-06 07:14:06.612594"]]
   (1.4ms)  commit transaction
=> true

もちろん、ユーザーに紐ついていない状態なので保存されない。こいつを工夫してみる

Postインスタンスをごにょごにょ.rb
user.posts.push(post)
   (0.1ms)  begin transaction
  SQL (0.3ms)  UPDATE "posts" SET "updated_at" = ?, "user_id" = ? WHERE "posts"."id" = 3  [["updated_at", "2015-10-06 07:15:16.168755"], ["user_id", 1]]
   (1.4ms)  commit transaction
=> [#<Post id: 1, text: "万策尽きた〜", user_id: 1, created_at: "2015-10-06 07:08:44", updated_at: "2015-10-06 07:08:44">,
 #<Post id: 2, text: "どんどんどーなっつどーんといこー!", user_id: 1, created_at: "2015-10-06 07:08:44", updated_at: "2015-10-06 07:08:44">,
 #<Post id: 3, text: "上手くいかないことを人のせいにしているようなヤツは辞めちまえよ!", user_id: 1, created_at: "2015-10-06 07:14:06", updated_at: "2015-10-06 07:15:16">]

ary#push は破壊的変更が発生するので、その時点でpostインスタンスがuserインスタンスに紐ついたので、post.user_id が更新され、DBに保存されます。

fmfm :thought_balloon:
なるほどなぁ。

まとめ

関連モデルはもちろん便利だけれど、user.posts.new とかそういうのは気をつけて使ったほうが良さそう。特に user に紐ついたインスタンスまでまとめて保存されるのは、忘れないようにするために工夫は必要ですね。

1
1
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
1
1