🗂

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

2022/04/14に公開

多対多

よくわからないので一個づつ migrate を試していく
MySQLだけまっさらな状態に戻して、migrateは流用するので、 rails db:migrate VERSION=... で一個づつ指定して確認していきます。

1対多 を改めて 1Step づつ ERD していく

最初の user table だけの時。

class CreateUsers < ActiveRecord::Migration[7.0]
  def change
    create_table :users do |t|
      t.string :name
      t.string :email

      t.timestamps
    end
  end
end
  • 上記を rails db:migrate した後の erd

post を追加してみる

class CreatePosts < ActiveRecord::Migration[7.0]
  def change
    create_table :posts do |t|
      t.text :content
      t.references :user, null: false, foreign_key: true

      t.timestamps
    end
    add_index :posts, [:user_id, :created_at]
  end
end
  • 上記を rails db:migrate した後の erd

    • か、変わらんっ...!
  • ので、 model にそれぞれ追加

class User < ApplicationRecord
  has_many :posts
end
class Post < ApplicationRecord
  belongs_to :user
end
  • あらたえめて erd
    • model に入れないとだめぽそ
    • migrate の時の references でよろしくやってくれるんだと思ってたよ...。
    • migrate した時には model ファイルがなかったので、その為かもしれないけど()

多対多 に続く...。

現状 user table と post table でリレーションを作ってる状態。
これに group table を追加して、下記を実現できるようにしてみる。

  • 1つのグループに複数のユーザが所属できる
  • 1人のユーザは複数のグループに所属できる

group table の migrate

class CreateGroups < ActiveRecord::Migration[7.0]
  def change
    create_table :groups do |t|
      t.string :name

      t.timestamps
    end
  end
end
class Group < ApplicationRecord
end
  • 上記を rails db:migrate した後の erd
    • group table が生えた

中間テーブルを作る

  • なんだか昔は user と group の中間であれば user_group table 的な感じだったようだ
  • そもそも中間テーブルの命名考えるの苦手ですし... って、思いたい気持ちはある
class CreateGroupMembers < ActiveRecord::Migration[7.0]
  def change
    create_table :group_members do |t|
      t.references :user, null: false, foreign_key: true
      t.references :group,null: false, foreign_key: true

      t.timestamps
    end
    add_index :group_members, [:user_id, :group_id], unique: true
  end
end
  • 上記を rails db:migrate した後の erd

    • groupMember table が生えた
  • Model user , group に手を入れる

class User < ApplicationRecord
  has_many :posts
  has_many :group_members, class_name: "GroupMember",
           foreign_key: "user_id"
end
class Group < ApplicationRecord
  has_many :group_members, class_name: "GroupMember",
           foreign_key: "group_id"
end
  • 改めて erd した。
    • 画像が N:N に見えない不思議

ここまでに作った table に rails c でアレコレしてみる。

一旦 データを作成

  • User 作成
    • 気持ちは Web で新規ユーザ作成した気持ちで
pry(main)> User.create(name: "nyasu", email: "nyasu@example.com")
pry(main)> User.create(name: "nya", email: "nya@example.com")
pry(main)> User.all
  User Load (4.7ms)  SELECT `users`.* FROM `users`
=> [#<User:0x000000010990f240 id: 1, name: "nyasu", email: "nyasu@example.com", created_at: Wed, 13 Apr 2022 13:37:03.816365000 UTC +00:00, updated_at: Wed, 13 Apr 2022 13:37:03.816365000 UTC +00:00>,
 #<User:0x000000010990f178 id: 2, name: "nya", email: "nya@example.com", created_at: Wed, 13 Apr 2022 13:37:12.864565000 UTC +00:00, updated_at: Wed, 13 Apr 2022 13:37:12.864565000 UTC +00:00>]
  • Group 作成
    • 気持ちは Web で新規に作成したユーザがグループも作成した気持ちで
    • こういう時に適当に入力する内容でセンスって分かりますよね()
pry(main)> Group.create(name: "nyanyanya")
pry(main)> Group.all
  Group Load (4.1ms)  SELECT `groups`.* FROM `groups`
=> [#<Group:0x0000000109a3dc48 id: 1, name: "nyanyanya", created_at: Wed, 13 Apr 2022 13:40:02.670938000 UTC +00:00, updated_at: Wed, 13 Apr 2022 13:40:02.670938000 UTC +00:00>]
  • と、別々に関連なくそれぞれができてしまう...。

  • model 改変

class User < ApplicationRecord
  has_many :posts
  has_many :group_members, foreign_key: "user_id"
  has_many :groups, through: :group_members, source: :group
end
class Group < ApplicationRecord
  has_many :group_members, foreign_key: "group_id"
  has_many :users, through: :group_members, source: :user
end
class GroupMember < ApplicationRecord
  belongs_to :user
  belongs_to :group
end
  • rails c で改めて。
pry(main)> user = User.find(1)
  User Load (1.7ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
=> #<User:0x000000010f191558 id: 1, name: "nyasu", email: "nyasu@example.com", created_at: Wed, 13 Apr 2022 13:37:03.816365000 UTC +00:00, updated_at: Wed, 13 Apr 2022 13:37:03.816365000 UTC +00:00>

user 経由で group を作成する

pry(main)> user.groups.create(name: "nya-n")
  TRANSACTION (2.8ms)  BEGIN
  Group Create (2.8ms)  INSERT INTO `groups` (`name`, `created_at`, `updated_at`) VALUES ('nya-n', '2022-04-13 14:18:09.867220', '2022-04-13 14:18:09.867220')
  User Load (3.0ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
  GroupMember Create (2.7ms)  INSERT INTO `group_members` (`user_id`, `group_id`, `created_at`, `updated_at`) VALUES (1, 2, '2022-04-13 14:18:09.894603', '2022-04-13 14:18:09.894603')
  TRANSACTION (4.2ms)  COMMIT
=> #<Group:0x000000010fb1eeb8 id: 2, name: "nya-n", created_at: Wed, 13 Apr 2022 14:18:09.867220000 UTC +00:00, updated_at: Wed, 13 Apr 2022 14:18:09.867220000 UTC +00:00>

MySQL を確認

  • 中感テーブルにもデータが入ってる
mysql> select * from users;
+----+-------+-------------------+----------------------------+----------------------------+
| id | name  | email             | created_at                 | updated_at                 |
+----+-------+-------------------+----------------------------+----------------------------+
|  1 | nyasu | nyasu@example.com | 2022-04-13 13:37:03.816365 | 2022-04-13 13:37:03.816365 |
|  2 | nya   | nya@example.com   | 2022-04-13 13:37:12.864565 | 2022-04-13 13:37:12.864565 |
+----+-------+-------------------+----------------------------+----------------------------+
2 rows in set (0.01 sec)

mysql> select * from groups;
+----+-----------+----------------------------+----------------------------+
| id | name      | created_at                 | updated_at                 |
+----+-----------+----------------------------+----------------------------+
|  1 | nyanyanya | 2022-04-13 13:40:02.670938 | 2022-04-13 13:40:02.670938 |
|  2 | nya-n     | 2022-04-13 14:18:09.867220 | 2022-04-13 14:18:09.867220 |
+----+-----------+----------------------------+----------------------------+
2 rows in set (0.01 sec)

mysql> select * from group_members;
+----+---------+----------+----------------------------+----------------------------+
| id | user_id | group_id | created_at                 | updated_at                 |
+----+---------+----------+----------------------------+----------------------------+
|  1 |       1 |        2 | 2022-04-13 14:18:09.894603 | 2022-04-13 14:18:09.894603 |
+----+---------+----------+----------------------------+----------------------------+
1 row in set (0.00 sec)

mysql> 

rails c で確認( group 視点 )

pry(main)> group = Group.find(2)
  Group Load (4.4ms)  SELECT `groups`.* FROM `groups` WHERE `groups`.`id` = 2 LIMIT 1
=> #<Group:0x000000010fa178a8 id: 2, name: "nya-n", created_at: Wed, 13 Apr 2022 14:18:09.867220000 UTC +00:00, updated_at: Wed, 13 Apr 2022 14:18:09.867220000 UTC +00:00>
  • nya-n group に所属している users を問い合わせると、現在所属している nyasu を取得できる
pry(main)> group.users
  User Load (3.6ms)  SELECT `users`.* FROM `users` INNER JOIN `group_members` ON `users`.`id` = `group_members`.`user_id` WHERE `group_members`.`group_id` = 2
=> [#<User:0x000000010f7bebd8 id: 1, name: "nyasu", email: "nyasu@example.com", created_at: Wed, 13 Apr 2022 13:37:03.816365000 UTC +00:00, updated_at: Wed, 13 Apr 2022 13:37:03.816365000 UTC +00:00>]

nyasu があらたに groupB を立ち上げた...!

pry(main)> user.groups.create(name: "groupB")
  TRANSACTION (3.9ms)  BEGIN
  Group Create (3.0ms)  INSERT INTO `groups` (`name`, `created_at`, `updated_at`) VALUES ('groupB', '2022-04-13 14:27:04.309306', '2022-04-13 14:27:04.309306')
  User Load (3.7ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
  GroupMember Create (3.2ms)  INSERT INTO `group_members` (`user_id`, `group_id`, `created_at`, `updated_at`) VALUES (1, 3, '2022-04-13 14:27:04.325420', '2022-04-13 14:27:04.325420')
  TRANSACTION (6.8ms)  COMMIT
=> #<Group:0x000000010f766078 id: 3, name: "groupB", created_at: Wed, 13 Apr 2022 14:27:04.309306000 UTC +00:00, updated_at: Wed, 13 Apr 2022 14:27:04.309306000 UTC +00:00>

nyasu が自分が所属する group 一覧を問い合わせた...!

pry(main)> user.groups
  Group Load (5.0ms)  SELECT `groups`.* FROM `groups` INNER JOIN `group_members` ON `groups`.`id` = `group_members`.`group_id` WHERE `group_members`.`user_id` = 1
=> [#<Group:0x000000010fb1eeb8 id: 2, name: "nya-n", created_at: Wed, 13 Apr 2022 14:18:09.867220000 UTC +00:00, updated_at: Wed, 13 Apr 2022 14:18:09.867220000 UTC +00:00>,
 #<Group:0x000000010f766078 id: 3, name: "groupB", created_at: Wed, 13 Apr 2022 14:27:04.309306000 UTC +00:00, updated_at: Wed, 13 Apr 2022 14:27:04.309306000 UTC +00:00>]

groupB に新たなメンバ nya が増えた...!

pry(main)> user = User.find_by(name: "nya")
pry(main)> group = Group.find_by(name: "groupB")
pry(main)> GroupMember.create(user_id: user.id, group_id: group.id)
  TRANSACTION (4.9ms)  BEGIN
  User Load (4.1ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 2 LIMIT 1
  Group Load (3.3ms)  SELECT `groups`.* FROM `groups` WHERE `groups`.`id` = 3 LIMIT 1
  GroupMember Create (3.2ms)  INSERT INTO `group_members` (`user_id`, `group_id`, `created_at`, `updated_at`) VALUES (2, 3, '2022-04-14 12:39:22.344590', '2022-04-14 12:39:22.344590')
  TRANSACTION (6.0ms)  COMMIT
=> #<GroupMember:0x0000000110518810 id: 5, user_id: 2, group_id: 3, created_at: Thu, 14 Apr 2022 12:39:22.344590000 UTC +00:00, updated_at: Thu, 14 Apr 2022 12:39:22.344590000 UTC +00:00>
pry(main)> 
  • 所属しているっ...!
pry(main)> user.groups
  Group Load (4.0ms)  SELECT `groups`.* FROM `groups` INNER JOIN `group_members` ON `groups`.`id` = `group_members`.`group_id` WHERE `group_members`.`user_id` = 2
=> [#<Group:0x000000010ec856b8 id: 3, name: "groupB", created_at: Wed, 13 Apr 2022 14:27:04.309306000 UTC +00:00, updated_at: Wed, 13 Apr 2022 14:27:04.309306000 UTC +00:00>]
pry(main)> 

しかし、即辞めた...

pry(main)> GroupMember.find_by(user_id: user.id, group_id: group.id).destroy
  GroupMember Load (4.5ms)  SELECT `group_members`.* FROM `group_members` WHERE `group_members`.`user_id` = 2 AND `group_members`.`group_id` = 3 LIMIT 1
  TRANSACTION (2.9ms)  BEGIN
  GroupMember Destroy (4.7ms)  DELETE FROM `group_members` WHERE `group_members`.`id` = 5
  TRANSACTION (6.8ms)  COMMIT
=> #<GroupMember:0x000000010fda8bc0 id: 5, user_id: 2, group_id: 3, created_at: Thu, 14 Apr 2022 12:39:22.344590000 UTC +00:00, updated_at: Thu, 14 Apr 2022 12:39:22.344590000 UTC +00:00>
pry(main)> 
  • nya は1人になった
pry(main)> user = User.find_by(name: "nya")
  User Load (4.6ms)  SELECT `users`.* FROM `users` WHERE `users`.`name` = 'nya' LIMIT 1
=> #<User:0x000000010fb12460 id: 2, name: "nya", email: "nya@example.com", created_at: Wed, 13 Apr 2022 13:37:12.864565000 UTC +00:00, updated_at: Wed, 13 Apr 2022 13:37:12.864565000 UTC +00:00>
pry(main)> user.groups
  Group Load (4.1ms)  SELECT `groups`.* FROM `groups` INNER JOIN `group_members` ON `groups`.`id` = `group_members`.`group_id` WHERE `group_members`.`user_id` = 2
=> []
pry(main)> 

Discussion