Posts 2モデル間の1:1関連
Post
Cancel

2モデル間の1:1関連

Railsのモデル間の関連を具体的に作成します。関連には、2つのモデル間の関連以外に一つのモデルのインスタンス間(テーブルのデータ間)の関連、いわゆる自己関連というものもあります。

ここでは、2つのモデル間に1対1の関連を作りRails consoleで動作を確認します。

1対多関連については2モデル間の1:多関連を参照してください。

1対多関連については2モデル間の多:多関連を参照してください。

自己関連については自己関連(自己結合)を参照してください。

Model

今回、登場するモデルは、Userモデル と Roomモデル の2つです。 テーブルでいえば、usersテーブル と roomsテーブル です。

2モデル間の1対1関連

共同利用ができる部屋(会議室や体育館のような)があって、その部屋(代表としてここでは会議室ということにします)を管理する責任者が一人だけ担当しているという状況を考えてみます。 この場合、会議室(the_room)の責任者(room_host)は一人で、担当できる会議室は一つだけです。このような関係を1対1関連というのでした。

したがって、

  • the_room : room_host は、1対1関連

となります。

UserモデルとRoomモデル

Userモデル と Roomモデル の関連を以下のように作成します。

  • UserモデルとRoomモデルを作る

references を使ってフィールドを作ると、indexもつけてくれます。 Railsアプリケーションを作ったディレクトリで

$ rails g model user name:string password:string
$ rails g model room name:string user:references
$ rake db:migrate

を実行しモデルを作成します。

次に各モデルに以下を追加記述します。ここで、参照相手が入るuser_idフィールドを持つ(user:referencesの指定で作成されました)Roomモデルの方に belongs_to を記述します。反対のUserモデルの側には「1対1」関連なので has_one を記述します。

  • user.rb
class User < ActiveRecord::Base
  has_one :the_room, :class_name => "Room"
   ...
end

会議室担当者となる人は「the_room」属性を通して担当する会議室を参照できます。 クラス名の単数形を使って has_one :room と指定するときには :class_name => “Room” は省略できます。

class User < ActiveRecord::Base
  has_one :room
   ...
end
  • room.rb
class Room < ActiveRecord::Base
  belongs_to :room_host, :class_name => "User", :foreign_key=>"user_id"
   ...
end

会議室の担当者を参照するのが「room_host」属性です。 クラス名の単数形を使って belongs_to :user と指定するときには :class_name => “User”, :foreign_key=>”user_id” は省略できます。

class Room < ActiveRecord::Base
  belongs_to :user
   ...
end

クラス図はこのようになります。

Rails console で確認

Rails console で確認してみます。

会議室に担当者を関連付ける(担当者user1が先に作られている)場合と、担当者に会議室を関連付ける(会議室room2が先に作られている)場合の両方を確認します。

  1. user1を作成し、そのuser1を担当者とする会議室room1を作成します。
  2. user1.the_roomで会議室が取得でき、逆にroom1.room_hostで担当者が取得できることを確認します。
  3. 今度は、room2を作りますが、担当者はまだいません。
  4. user2を作成すると同時に、room2を担当する会議室に指定します。
  5. user2.the_roomで会議室が取得でき、逆にroom2.room_hostで担当者が取得できることを確認します。

では始めましょう。

rails consoleコマンドに –sandbox オプションをつけると、終了時にデータベースに行った作業をロールバックして初めの状態に戻してくれます。

また、Hirbを開始して出力を見やすくします。

$ rails console --sandbox
Loading development environment in sandbox (Rails 4.1.8)
Any modifications you make will be rolled back on exit
irb(main):001:0> Hirb.enable
=> true

user1を作成します。 room1を作成するときにuser1を担当者とします。

コンソールに打ち込んだのは次のものです。

  1. user1 = User.create(name: ‘ユーザー1’, password: ‘user1’)
  2. room1 = Room.create(name: ‘第一会議室’, room_host: user1)

room_host 属性を使ってUserモデルのインスタンスを関連付けしていることに着目してください。

irb(main):002:0> user1 = User.create(name: 'ユーザー1', password: 'user1')

  SQL (0.3ms)  INSERT INTO "users" ("created_at", "name", "password", "updated_at") VALUES (?, ?, ?, ?)  [["created_at", "2015-04-09 04:10:52.171886"], ["name", "ユーザー1"], ["password", "user1"], ["updated_at", "2015-04-09 04:10:52.171886"]]

+----+----------+----------+-------------------------+-------------------------+
| id | name     | password | created_at              | updated_at              |
+----+----------+----------+-------------------------+-------------------------+
| 1  | ユーザー1 | user1    | 2015-04-09 04:10:52 UTC | 2015-04-09 04:10:52 UTC |
+----+----------+----------+-------------------------+-------------------------+
1 row in set
irb(main):003:0> room1 = Room.create(name: '第一会議室', room_host: user1)

  SQL (0.2ms)  INSERT INTO "rooms" ("created_at", "name", "updated_at", "user_id") VALUES (?, ?, ?, ?)  [["created_at", "2015-04-09 04:12:10.712924"], ["name", "第一会議室"], ["updated_at", "2015-04-09 04:12:10.712924"], ["user_id", 1]]

+----+------------+---------+-------------------------+-------------------------+
| id | name       | user_id | created_at              | updated_at              |
+----+------------+---------+-------------------------+-------------------------+
| 1  | 第一会議室 | 1       | 2015-04-09 04:12:10 UTC | 2015-04-09 04:12:10 UTC |
+----+------------+---------+-------------------------+-------------------------+
1 row in set

では、user1とroom1の関連を確認しましょう。

コンソールに打ち込んだのは次のものです。

  1. user1.the_room
  2. room1.room_host
irb(main):004:0> user1.the_room

  Room Load (0.2ms)  SELECT  "rooms".* FROM "rooms"  WHERE "rooms"."user_id" = ? LIMIT 1  [["user_id", 1]]

+----+------------+---------+-------------------------+-------------------------+
| id | name       | user_id | created_at              | updated_at              |
+----+------------+---------+-------------------------+-------------------------+
| 1  | 第一会議室 | 1       | 2015-04-09 04:12:10 UTC | 2015-04-09 04:12:10 UTC |
+----+------------+---------+-------------------------+-------------------------+
1 row in set
irb(main):005:0> room1.room_host

+----+----------+----------+-------------------------+-------------------------+
| id | name     | password | created_at              | updated_at              |
+----+----------+----------+-------------------------+-------------------------+
| 1  | ユーザー1 | user1    | 2015-04-09 04:10:52 UTC | 2015-04-09 04:10:52 UTC |
+----+----------+----------+-------------------------+-------------------------+
1 row in set

それぞれの参照先を取得できています。

先ほどはuser1を作り、その後でroom1を作り関連付けました。今度は逆にroom2を先に作り、その後でuser2を作り関連付けます。

コンソールに打ち込んだのは次のものです。

  1. room2 = Room.create(name: ‘第二会議室’)
  2. user2 = User.create(name: ‘ユーザー2’, password: ‘user2’, the_room: room2)

今度は、the_room属性を使ってRoomモデルのインスタンスを関連付けしていることに着目してください。

irb(main):007:0> room2 = Room.create(name: '第二会議室')

  SQL (0.1ms)  INSERT INTO "rooms" ("created_at", "name", "updated_at") VALUES (?, ?, ?)  [["created_at", "2015-04-09 05:52:53.513565"], ["name", "第二会議室"], ["updated_at", "2015-04-09 05:52:53.513565"]]

+----+------------+---------+-------------------------+-------------------------+
| id | name       | user_id | created_at              | updated_at              |
+----+------------+---------+-------------------------+-------------------------+
| 2  | 第二会議室 |         | 2015-04-09 05:52:53 UTC | 2015-04-09 05:52:53 UTC |
+----+------------+---------+-------------------------+-------------------------+
1 row in set
irb(main):008:0> user2 = User.create(name: 'ユーザー2', password: 'user2', the_room: room2)

  SQL (0.1ms)  INSERT INTO "users" ("created_at", "name", "password", "updated_at") VALUES (?, ?, ?, ?)  [["created_at", "2015-04-09 06:09:21.951642"], ["name", "ユーザー2"], ["password", "user2"], ["updated_at", "2015-04-09 06:09:21.951642"]]
  SQL (0.1ms)  UPDATE "rooms" SET "updated_at" = ?, "user_id" = ? WHERE "rooms"."id" = 2  [["updated_at", "2015-04-09 06:09:21.952648"], ["user_id", 3]]

+----+----------+----------+-------------------------+-------------------------+
| id | name     | password | created_at              | updated_at              |
+----+----------+----------+-------------------------+-------------------------+
| 3  | ユーザー2 | user2    | 2015-04-09 06:09:21 UTC | 2015-04-09 06:09:21 UTC |
+----+----------+----------+-------------------------+-------------------------+
1 row in set

参照を確認します。

コンソールに打ち込んだのは次のものです。

  1. user2.the_room
  2. room2.room_host
irb(main):009:0> user2.the_room
+----+------------+---------+-------------------------+-------------------------+
| id | name       | user_id | created_at              | updated_at              |
+----+------------+---------+-------------------------+-------------------------+
| 2  | 第二会議室 | 3       | 2015-04-09 05:52:53 UTC | 2015-04-09 06:09:21 UTC |
+----+------------+---------+-------------------------+-------------------------+
1 row in set
irb(main):010:0> room2.room_host
  User Load (0.1ms)  SELECT  "users".* FROM "users"  WHERE "users"."id" = ? LIMIT 1  [["id", 3]]
+----+----------+----------+-------------------------+-------------------------+
| id | name     | password | created_at              | updated_at              |
+----+----------+----------+-------------------------+-------------------------+
| 3  | ユーザー2 | user2    | 2015-04-09 06:09:21 UTC | 2015-04-09 06:09:21 UTC |
+----+----------+----------+-------------------------+-------------------------+
1 row in set

正しく参照できています。

なんて簡単!!やっぱりRailsはすばらしい。

シェア
#内容発言者

自己関連(自己結合)

2モデル間の1:多関連