はじめに
これまで1対1の関連付けをしたことがなかったので
実装して検証してみました。
動機
私の場合は、ユーザーの認証機能にdevise_token_authのGemを用いており
それによって、Usersテーブルに認証に関わるカラムが増えてしまいました。
そこで、以下の通り、責務を分けてDB設計をしました。
- 認証: Userモデル
- ユーザー特性: Profileモデル
DB設計において、テーブルを分けるとクエリ数が増えてしまい
パフォーマンスが落ちますが、
一方で、自分の可読性を考慮すると1テーブルに集約させすぎるのも…
と考え、この設計にしました。
作業内容
- モデル関連付け
- 検証1: テーブルが正しく作成できているか
- テストデータ作成
- 検証2: 関連付けが正しくできているか
前提
devise_token_authで認証機能を実装時にUsersモデルを作成済み
モデル関連付け
Railsガイドを参考に実装していきます。
ターミナル
# generateコマンドでマイグレーションファイルとモデルを生成する
% rails g model profile
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
root/app/models/profile.rb
class Profile < ApplicationRecord
# ProfileはUserに従属する
belongs_to :user
end
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を1つしか持たない
has_one :profile, dependent: :destroy
--- 省略 ---
end
ターミナル
# マイグレーションを実行してDBに反映させる
% rails db:migrate
検証1: テーブルが正しく作成できているか
ターミナル
# MySQLにrootユーザーで接続する
% mysql -u root -pパスワード
# 作成済みのデータベースを確認
% show databases;
# 使用するデータベースを選択
% use 対象データベース名;
# 作成済みのテーブルを確認
% show tables;
# Profilesのカラムが正しく設定できているか確認
% desc profiles;
# Profilesテーブルの中身が空である事を確認
% select * from profiles;
# MySQLから抜ける
% exit
テストデータ作成
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: 'はじめまして...',
--- 他カラム ---
)
ターミナル
# テストデータをDBに流す
% rails db:seed
検証2: 関連付けが正しくできているか
ターミナル
# 検証のためにコンソールに入る
% 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で実装することにも慣れてないなー
ポートフォリオ完成にいつになるのやら…
最後までお読み頂き、ありがとうございました!