👌

Associationを試す...!(1:多)

2022/03/21に公開

リレーションはよく使うよね。

けど、理解が曖昧な感じがするので数打って理解する

一つ目の User Model を作成する

  • User Model を作成する
rails g model User name:string email:string
      invoke  active_record
      create    db/migrate/20220227085625_create_users.rb
      create    app/models/user.rb
rails db:migrate

と、こうなる。

mysql> desc users;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| name       | varchar(255) | YES  |     | NULL    |                |
| email      | varchar(255) | YES  |     | NULL    |                |
| created_at | datetime(6)  | NO   |     | NULL    |                |
| updated_at | datetime(6)  | NO   |     | NULL    |                |
+------------+--------------+------+-----+---------+----------------+
5 rows in set (0.01 sec)
  • お試しにデータ登録
[1] pry(main)> user = User.new(name: "nyasu", email: "nyasu@example.com")
=> #<User:0x000000010a1661f0 id: nil, name: "nyasu", email: "nyasu@example.com", created_at: nil, updated_at: nil>
[2] pry(main)> user.save
  TRANSACTION (4.9ms)  BEGIN
  User Create (5.0ms)  INSERT INTO `users` (`name`, `email`, `created_at`, `updated_at`) VALUES ('nyasu', 'nyasu@example.com', '2022-03-21 10:46:53.028514', '2022-03-21 10:46:53.028514')
  TRANSACTION (23.4ms)  COMMIT
=> true

関連付けて post を作成する

rails g model post content:text user:references
      invoke  active_record
      create    db/migrate/20220321094237_create_posts.rb
      create    app/models/post.rb
rails db:migrate

と、こうなる

mysql> desc posts;
+------------+-------------+------+-----+---------+----------------+
| Field      | Type        | Null | Key | Default | Extra          |
+------------+-------------+------+-----+---------+----------------+
| id         | bigint(20)  | NO   | PRI | NULL    | auto_increment |
| content    | text        | YES  |     | NULL    |                |
| user_id    | bigint(20)  | NO   | MUL | NULL    |                |
| created_at | datetime(6) | NO   |     | NULL    |                |
| updated_at | datetime(6) | NO   |     | NULL    |                |
+------------+-------------+------+-----+---------+----------------+
5 rows in set (0.01 sec)
  • 関連付けを利用して .build で登録する
[1] pry(main)> user.post.build(content: "nyas comment")
=> #<Post:0x000000010a1debf0 id: nil, content: "nyas comment", user_id: 2, created_at: nil, updated_at: nil>
[2] pry(main)> user.post.build(content: "nyas comment2")
=> #<Post:0x000000010a2e6340 id: nil, content: "nyas comment2", user_id: 2, created_at: nil, updated_at: nil>
[3] pry(main)> user.post.build(content: "nyas comment3")
=> #<Post:0x000000010a32e758 id: nil, content: "nyas comment3", user_id: 2, created_at: nil, updated_at: nil>
  • nyasu user の post 一覧を取得する
[4] pry(main)> user.posts
=> [#<Post:0x000000010a1debf0 id: nil, content: "nyas comment", user_id: 2, created_at: nil, updated_at: nil>,
 #<Post:0x000000010a2e6340 id: nil, content: "nyas comment2", user_id: 2, created_at: nil, updated_at: nil>,
 #<Post:0x000000010a32e758 id: nil, content: "nyas comment3", user_id: 2, created_at: nil, updated_at: nil>]
  • 上記の状態で rails c を再度立ち上げなおすと登録した post データが存在しない...!
    • これは、 .build が Model.new に該当する為なので、別途保存しておかないとダメ。
[1] pry(main)> user = User.find_by(name: "nyasu")
  User Load (4.5ms)  SELECT `users`.* FROM `users` WHERE `users`.`name` = 'nyasu' LIMIT 1
=> #<User:0x0000000114ac9198 id: 2, name: "nyasu", email: "nyasu@example.com", created_at: Mon, 21 Mar 2022 10:46:53.028514000 UTC +00:00, updated_at: Mon, 21 Mar 2022 10:46:53.028514000 UTC +00:00>
[2] pry(main)> user.posts
  Post Load (7.3ms)  SELECT `posts`.* FROM `posts` WHERE `posts`.`user_id` = 2
=> []
  • あらためて .create で登録する
    • 今度は同時に SQL query も発行され、Commitまで完了しているのでデータも登録されているのがログからもわかる。
[5] pry(main)> user.post.create(content: "nyasu comment")
  TRANSACTION (4.2ms)  BEGIN
  Post Create (5.1ms)  INSERT INTO `posts` (`content`, `user_id`, `created_at`, `updated_at`) VALUES ('nyasu comment', 2, '2022-03-21 10:54:27.497592', '2022-03-21 10:54:27.497592')
  TRANSACTION (19.9ms)  COMMIT
=> #<Post:0x0000000114c80298 id: 2, content: "nyasu comment", user_id: 2, created_at: Mon, 21 Mar 2022 10:54:27.497592000 UTC +00:00, updated_at: Mon, 21 Mar 2022 10:54:27.497592000 UTC +00:00>
[6] pry(main)> user.post.create(content: "nyasu comment2")
  TRANSACTION (3.5ms)  BEGIN
  Post Create (4.7ms)  INSERT INTO `posts` (`content`, `user_id`, `created_at`, `updated_at`) VALUES ('nyasu comment2', 2, '2022-03-21 10:54:31.490075', '2022-03-21 10:54:31.490075')
  TRANSACTION (6.3ms)  COMMIT
=> #<Post:0x0000000114ea41c8 id: 3, content: "nyasu comment2", user_id: 2, created_at: Mon, 21 Mar 2022 10:54:31.490075000 UTC +00:00, updated_at: Mon, 21 Mar 2022 10:54:31.490075000 UTC +00:00>
[7] pry(main)> user.post.create(content: "nyasu comment3")
  TRANSACTION (4.5ms)  BEGIN
  Post Create (5.2ms)  INSERT INTO `posts` (`content`, `user_id`, `created_at`, `updated_at`) VALUES ('nyasu comment3', 2, '2022-03-21 10:54:33.077482', '2022-03-21 10:54:33.077482')
  TRANSACTION (8.6ms)  COMMIT
=> #<Post:0x0000000114effeb0 id: 4, content: "nyasu comment3", user_id: 2, created_at: Mon, 21 Mar 2022 10:54:33.077482000 UTC +00:00, updated_at: Mon, 21 Mar 2022 10:54:33.077482000 UTC +00:00>
[8] pry(main)> 
% rails c
Loading development environment (Rails 7.0.2.2)
[1] pry(main)> user = User.find_by(name: "nyasu")
  User Load (3.1ms)  SELECT `users`.* FROM `users` WHERE `users`.`name` = 'nyasu' LIMIT 1
=> #<User:0x000000010a4894b8 id: 2, name: "nyasu", email: "nyasu@example.com", created_at: Mon, 21 Mar 2022 10:46:53.028514000 UTC +00:00, updated_at: Mon, 21 Mar 2022 10:46:53.028514000 UTC +00:00>
[2] pry(main)> user.posts
  Post Load (3.6ms)  SELECT `posts`.* FROM `posts` WHERE `posts`.`user_id` = 2
=> [#<Post:0x000000010a8308a0 id: 2, content: "nyasu comment", user_id: 2, created_at: Mon, 21 Mar 2022 10:54:27.497592000 UTC +00:00, updated_at: Mon, 21 Mar 2022 10:54:27.497592000 UTC +00:00>,
 #<Post:0x000000010a920ad0 id: 3, content: "nyasu comment2", user_id: 2, created_at: Mon, 21 Mar 2022 10:54:31.490075000 UTC +00:00, updated_at: Mon, 21 Mar 2022 10:54:31.490075000 UTC +00:00>,
 #<Post:0x000000010a9208f0 id: 4, content: "nyasu comment3", user_id: 2, created_at: Mon, 21 Mar 2022 10:54:33.077482000 UTC +00:00, updated_at: Mon, 21 Mar 2022 10:54:33.077482000 UTC +00:00>]
[3] pry(main)> 
  • posts table を確認してもわかる。
mysql> select * from posts;
+----+----------------+---------+----------------------------+----------------------------+
| id | content        | user_id | created_at                 | updated_at                 |
+----+----------------+---------+----------------------------+----------------------------+
|  1 | nyasu          |       1 | 2022-03-21 10:10:18.669882 | 2022-03-21 10:10:18.669882 |
|  2 | nyasu comment  |       2 | 2022-03-21 10:54:27.497592 | 2022-03-21 10:54:27.497592 |
|  3 | nyasu comment2 |       2 | 2022-03-21 10:54:31.490075 | 2022-03-21 10:54:31.490075 |
|  4 | nyasu comment3 |       2 | 2022-03-21 10:54:33.077482 | 2022-03-21 10:54:33.077482 |
+----+----------------+---------+----------------------------+----------------------------+
4 rows in set (0.01 sec)

結局リレーションってなんの為に...?

  • 関連付けをしておく事で、rails での Model clust に関連付け用の method が新しく生えて、楽に、便利に検索したり、データを生成したり、検索の仕方(SQL queryの使い方)に統一感をもたらす事でindexであったり、DB cacheであったりが用意になったりする様子。
  • また、削除する時にも関連データ 特定user を削除した時に、特定Userが登録したデータを合わせて削除するとかも楽になる。
  • 結局プロダクトでどういった表示、データ作成、検索、使われ方がされるのか次第なのかな?と、思います。
  • null 制約を付与する為に細かく table を分けたりする場合もあるようで、奥が深いなぁ。と思いました(小並)

手間をかけて投稿一覧を取得する

  • user を探す
[1] pry(main)> user = User.find_by(name: "nyasu")
  User Load (3.0ms)  SELECT `users`.* FROM `users` WHERE `users`.`name` = 'nyasu' LIMIT 1
=> #<User:0x0000000111f4ff58 id: 2, name: "nyasu", email: "nyasu@example.com", created_at: Mon, 21 Mar 2022 10:46:53.028514000 UTC +00:00, updated_at: Mon, 21 Mar 2022 10:46:53.028514000 UTC +00:00>
  • その後、該当ユーザの post を探す
[2] pry(main)> post = Post.where(user_id: user)
  Post Load (7.2ms)  SELECT `posts`.* FROM `posts` WHERE `posts`.`user_id` = 2
=> [#<Post:0x0000000112402f00 id: 2, content: "nyasu comment", user_id: 2, created_at: Mon, 21 Mar 2022 10:54:27.497592000 UTC +00:00, updated_at: Mon, 21 Mar 2022 10:54:27.497592000 UTC +00:00>,
 #<Post:0x0000000112402e38 id: 3, content: "nyasu comment2", user_id: 2, created_at: Mon, 21 Mar 2022 10:54:31.490075000 UTC +00:00, updated_at: Mon, 21 Mar 2022 10:54:31.490075000 UTC +00:00>,
 #<Post:0x0000000112402d70 id: 4, content: "nyasu comment3", user_id: 2, created_at: Mon, 21 Mar 2022 10:54:33.077482000 UTC +00:00, updated_at: Mon, 21 Mar 2022 10:54:33.077482000 UTC +00:00>]

楽に投稿一覧を探す

[7] pry(main)> user = User.find_by(name: "nyasu")
  User Load (4.9ms)  SELECT `users`.* FROM `users` WHERE `users`.`name` = 'nyasu' LIMIT 1
=> #<User:0x0000000112492358 id: 2, name: "nyasu", email: "nyasu@example.com", created_at: Mon, 21 Mar 2022 10:46:53.028514000 UTC +00:00, updated_at: Mon, 21 Mar 2022 10:46:53.028514000 UTC +00:00>

[8] pry(main)> user.posts
  Post Load (3.7ms)  SELECT `posts`.* FROM `posts` WHERE `posts`.`user_id` = 2
=> [#<Post:0x0000000112569768 id: 2, content: "nyasu comment", user_id: 2, created_at: Mon, 21 Mar 2022 10:54:27.497592000 UTC +00:00, updated_at: Mon, 21 Mar 2022 10:54:27.497592000 UTC +00:00>,
 #<Post:0x0000000112579078 id: 3, content: "nyasu comment2", user_id: 2, created_at: Mon, 21 Mar 2022 10:54:31.490075000 UTC +00:00, updated_at: Mon, 21 Mar 2022 10:54:31.490075000 UTC +00:00>,
 #<Post:0x0000000112578e98 id: 4, content: "nyasu comment3", user_id: 2, created_at: Mon, 21 Mar 2022 10:54:33.077482000 UTC +00:00, updated_at: Mon, 21 Mar 2022 10:54:33.077482000 UTC +00:00>]
[9] pry(main)> 

備考

  • 今回使った ER 図生成 ( Mac OS )
  • 下記を実行すると、 DB 情報を読み取って erd.pdf が生成される。
brew install graphviz
group :development, :test do
  gem 'rails-erd'
end
bundle exec erd

Discussion