Railsで1対1のモデル関連付けを学習する

# はじめに
これまで1対1の関連付けをしたことがなかったので
実装して検証してみました。



# 動機
私の場合は、ユーザーの認証機能にdevise_token_authのGemを用いており
それによって、Usersテーブルに認証に関わるカラムが増えてしまいました。

そこで、以下の通り、責務を分けてDB設計をしました。

* 認証：　Userモデル
* ユーザー特性：　Profileモデル

DB設計において、テーブルを分けるとクエリ数が増えてしまい
パフォーマンスが落ちますが、
一方で、自分の可読性を考慮すると1テーブルに集約させすぎるのも…
と考え、この設計にしました。



# 作業内容
* モデル関連付け
* 検証1：　テーブルが正しく作成できているか
* テストデータ作成
* 検証2：　関連付けが正しくできているか



# 前提
devise_token_authで認証機能を実装時にUsersモデルを作成済み



# モデル関連付け

Railsガイドを参考に実装していきます。

https://railsguides.jp/association_basics.html#belongs-to%E9%96%A2%E9%80%A3%E4%BB%98%E3%81%91

```zsh:ターミナル

# generateコマンドでマイグレーションファイルとモデルを生成する
% rails g model profile

```

```ruby:root/db/migrate/20220221xxxxxx_create_profiles.rb

class CreateProfiles < ActiveRecord::Migration[6.0]
  def change
    create_table :profiles do |t|
      # 生成するカラムを追加する
      t.belongs_to :user, index: { unique: true }, foreign_key: true
      t.string :gender
      t.string :residence
      t.text :introduction
      --- 他カラム... ----

      t.timestamps
    end
  end
end

```

```rust:root/app/models/profile.rb

class Profile < ApplicationRecord
  # ProfileはUserに従属する
  belongs_to :user
end

```


```ruby:root/app/models/user.rb

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
  include DeviseTokenAuth::Concerns::User

  --- 省略 ---
  # UserはProfileを１つしか持たない
  has_one :profile, dependent: :destroy
  --- 省略 ---

end
```

```zsh:ターミナル

# マイグレーションを実行してDBに反映させる
% rails db:migrate

```



# 検証1：　テーブルが正しく作成できているか

```zsh:ターミナル

# MySQLにrootユーザーで接続する
% mysql -u root -pパスワード

# 作成済みのデータベースを確認
% show databases;

# 使用するデータベースを選択
% use 対象データベース名;

# 作成済みのテーブルを確認
% show tables;

# Profilesのカラムが正しく設定できているか確認
% desc profiles;

# Profilesテーブルの中身が空である事を確認
% select * from profiles;

# MySQLから抜ける
% exit

```



# テストデータ作成

```ruby:root/db/seeds.rb

# テストユーザーを作成
User.create(
  name: 'Pokemon Sedai',
  email: 'monster@ball.com',
  password: 'pikachu1234',
  password_confirmation: 'pikachu1234'
)
User.create(
  name: 'Takeshi Iwark',
  email: 'ishi@tubute.com',
  password: 'pass1234',
  password_confirmation: 'pass1234'
)

# 上記ユーザーに紐づくプロフィールを作成
Profile.create(
  user_id: 1,
  gender: 'man',
  residence: 'Osaka',
  introduction: 'はじめまして...',
  --- 他カラム ---
)

```

```zsh:ターミナル

# テストデータをDBに流す
% rails db:seed

```


# 検証2：　関連付けが正しくできているか

```zsh:ターミナル

# 検証のためにコンソールに入る
% rails c --sandbox

# userという変数にテストユーザーのオブジェクトを入れる
> user = User.first

# profileという変数にテストユーザーのプロフィールを入れる
### (関連付けによって、Userモデルのオプジェクトにprofileメソッドが使える)
### (テストユーザーのプロフィールはseedで作成済み)
> profile = user.profile

# 格納したプロフィール情報を参照できる
> profile

# テストユーザーで新たにプロフィールを作成してみる
### (関連付けによってcreate_profileメソッドが使える)
> other_profile = user.create_profile(introduction: 'こんにちは...')

　↓

# しかし、テストユーザーのプロフィールは既に存在するため作成できない
### seedデータで作成しているので、Duplicate（重複）とエラーが出る
ActiveRecord::RecordNotUnique (Mysql2::Error: Duplicate entry '1' for key 'profiles.index_profiles_on_user_id')

# テストユーザー2のオブジェクトを作成
> other_user = User.second

# テストユーザー2は初めてプロフィールを作成するので成功する
### (テストユーザー2はseedでプロフィールを作成していない)
> profile = other_user.create_profile(introduction: 'こんにちは...')

```



# 終わりに
まだまだRailsで実装することにも慣れてないなー

ポートフォリオ完成にいつになるのやら…

最後までお読み頂き、ありがとうございました！