コード日進月歩

しんくうの技術的な小話、メモ、つれづれ、など

Rails で has_one の関係を作るとレコードの状態も has_oneを維持しようとする

has_oneにするとちゃんと維持しようとするんだと関心したのでメモ

環境

rails (5.1.6)

以下のようなモデルがあるとする

# == Schema Information
#
# Table name: users
#
#  id         :bigint(8)        not null, primary key
#  name       :string(255)      default(""), not null
#  created_at :datetime         not null
#  updated_at :datetime         not null
#

class User < ApplicationRecord
  has_one :profile
end
# == Schema Information
#
# Table name: profiles
#
#  id         :bigint(8)        not null, primary key
#  user_id    :bigint(8)
#  birthday   :datetime         not null
#  created_at :datetime         not null
#  updated_at :datetime         not null
#

class Profile < ApplicationRecord
  belongs_to :user , optional: true

end

こんな感じで変数としてcreateして

user = User.create(name:"test1")
   (0.4ms)  SET NAMES utf8mb4,  @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'),  @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
   (0.2ms)  BEGIN
  SQL (0.3ms)  INSERT INTO `users` (`name`, `created_at`, `updated_at`) VALUES ('test1', '2018-07-06 13:26:56', '2018-07-06 13:26:56')
   (0.7ms)  COMMIT
=> #<User id: 1, name: "test1", created_at: "2018-07-06 13:26:56", updated_at: "2018-07-06 13:26:56">

profile1 = Profile.create(birthday:Time.new(1990,1,10))
   (0.2ms)  BEGIN
  SQL (0.3ms)  INSERT INTO `profiles` (`birthday`, `created_at`, `updated_at`) VALUES ('1990-01-09 15:00:00', '2018-07-06 13:27:01', '2018-07-06 13:27:01')
   (1.2ms)  COMMIT
=> #<Profile id: 1, user_id: nil, birthday: "1990-01-09 15:00:00", created_at: "2018-07-06 13:27:01", updated_at: "2018-07-06 13:27:01">

代入すると入る

user.profile = profile1
  Profile Load (1.1ms)  SELECT  `profiles`.* FROM `profiles` WHERE `profiles`.`user_id` = 1 LIMIT 1
   (0.2ms)  BEGIN
  SQL (2.0ms)  UPDATE `profiles` SET `user_id` = 1, `updated_at` = '2018-07-06 13:27:07' WHERE `profiles`.`id` = 1
   (0.3ms)  COMMIT
=> #<Profile id: 1, user_id: 1, birthday: "1990-01-09 15:00:00", created_at: "2018-07-06 13:27:01", updated_at: "2018-07-06 13:27:07">

ここまでは普通の話なんですが 他の値を代入すると、ちゃんとIDのかき消し処理をする

profile2 = Profile.create(birthday:Time.new(1989,1,10))
   (0.2ms)  BEGIN
  SQL (2.0ms)  INSERT INTO `profiles` (`birthday`, `created_at`, `updated_at`) VALUES ('1989-01-09 15:00:00', '2018-07-06 13:28:36', '2018-07-06 13:28:36')
   (1.9ms)  COMMIT
=> #<Profile id: 2, user_id: nil, birthday: "1989-01-09 15:00:00", created_at: "2018-07-06 13:28:36", updated_at: "2018-07-06 13:28:36">

user.profile = profile2
   (0.3ms)  BEGIN
  SQL (0.6ms)  UPDATE `profiles` SET `user_id` = NULL, `updated_at` = '2018-07-06 13:28:46' WHERE `profiles`.`id` = 1
  SQL (0.4ms)  UPDATE `profiles` SET `user_id` = 1, `updated_at` = '2018-07-06 13:28:46' WHERE `profiles`.`id` = 2
   (0.4ms)  COMMIT
=> #<Profile id: 2, user_id: 1, birthday: "1989-01-09 15:00:00", created_at: "2018-07-06 13:28:36", updated_at: "2018-07-06 13:28:46">

ただし手動で入れるとそうはならないので注意

profile3 = Profile.create(user_id:user.id,birthday:Time.new(1989,1,10))
   (0.3ms)  BEGIN
  SQL (0.5ms)  INSERT INTO `profiles` (`user_id`, `birthday`, `created_at`, `updated_at`) VALUES (1, '1989-01-09 15:00:00', '2018-07-06 13:29:39', '2018-07-06 13:29:39')
   (1.0ms)  COMMIT
=> #<Profile id: 3, user_id: 1, birthday: "1989-01-09 15:00:00", created_at: "2018-07-06 13:29:39", updated_at: "2018-07-06 13:29:39">

Profile.where(user_id:user.id).size
   (0.6ms)  SELECT COUNT(*) FROM `profiles` WHERE `profiles`.`user_id` = 1
=> 2