はじめに
本記事では、業務でテーブルを作り直したいときにややこしかったことについて紹介していきます。
背景
開発を行なっていた際に、過去作成されていたテーブル名が機能改修後の機能と意味合いがずれており、今後開発で触れる時にわかりづらいということでテーブル名を変更したいと考えました。
- 既存のテーブルのテーブル名を変更したい
- 既存のテーブルにはリレーション関係がある
a. 子テーブルのテーブル名も親テーブルと合わせて変更したい
b. primary_keyやforeign_keyも変更する必要がある
→ テーブルを作り直した方がわかりやすいということで、作り直すことになりました。
ややこしかったところ
ややこしかったところは、下記で述べる「OldTableクラスとChildOfTableクラスをMigrationFileで定義しなくていけない理由はわかったけど、NewTableクラスとChildOfNewTableクラスは定義しなくていい」という点です。順を追って説明していきます。
以下のコードはレビューをいただく前のコードです。
class ChangeOldTableToNewTable < ActiveRecord::Migration[version]
def change
reversible do |r|
r.up do
create_table :new_table, primary_key: :new_table_id do |t|
...
end
add_foreign_key ...
create_table :child_of_new_table, primary_key :child_of_new_table do |t|
...
end
add_foreign_key ...
ActiveRecord::Base.transaction do
OldTable.all.each do |old|
# NewTableとChildOfNewTableへレコードをコピー
...
end
end
drop_table :old_table
drop_table :child_of_old_table
end
r.down do
# upと逆の処理
...
end
end
end
end
このコードには以下の問題点がありました。
問題点)私以外のエンジニアがmigrateするときにOldTableクラスとChildOfTableクラスが存在しないことです。
↓ 整理すると...
// 子テーブルも同様
<開発前>
OldModelFile
OldTable
MigrationFileなし
↓
<開発中>
OldModelFile
NewModelFile
OldTable
MigrationFileあり
↓
<開発完了時>
// OldModelFileがない -> OldTableクラスがないため、OldTable.all.eachのような処理が書けない
NewModelFile
OldTable
MigrationFileあり
↓
<migrationする>
NewModelFile
NewTable
MigrationFileあり
そのため、MigrationFile内でクラスを定義する必要があります。結論、この時定義するのはOldTableクラスとChildOfOldTableクラスのみで大丈夫です。
class ChangeOldTableToNewTable < ActiveRecord::Migration[version]
class OldTable < ApplicationRecord
self.table_name = :old_table
self.primary_key = :old_table_id
has_many ...
end
class ChildOfOldTable < ApplicationRecord
self.table_name = ...
self.primary_key = ...
belongs_to ...
end
def change
...
end
end
しかし、レビューをいただいた時は「NewTableクラスとかは定義しなくていいのはなんで??」と混乱しました。こういう時は実際に書き出してみて整理してみることが有効です。
<rollbackする>
NewTableModelFile
OldTable
MigrationFileあり
↓
<開発コミットを消す>
OldTableModelFile
OldTable
MigrationFileなし
整理してみたことで、rollbackする時にNewTableクラスとChildOfNewTableクラスは存在しているから定義する必要がないということがわかりました。
おまけ
開発に入った当初、開発経験があまりなく分からないことだらけでした。その内の1つを紹介します。
・migrationする度にbase-branchとの間でschemaに変更した覚えのない差分が生じる問題
問題点)
schemaがズレることで起こることとして以下のことが挙げられます。
- 本物のDB情報がわからなくなる
- git(主にステージング)が面倒くさくなる
- テストが通らなくなる
原因)前の開発等でDBの情報を変更し、そのまま次の開発に入ったから
開発でDBの情報を変更
↓
そのまま次の開発へ ← 原因はここ
↓
migration
↓
shemaにはDBの情報が反映される
↓
base-branchと差分が生じる
解決策)DBの情報をbase-branchに合わせれば良いので下記のようなSQL文を叩いてDBの情報を更新します。
-- テーブル削除(migration:rollbackでもいい)
drop table <テーブル名>;
-- カラム追加(NOT NULL制約をつけたい時はデータ型の後ろにnot nullをつける)
alter table <テーブル名> add column <カラム名> <データ型> after <追加するカラムの上にあるカラム名>;
-- カラム削除
alter table <テーブル名> drop column <カラム名>;
-- カラム名の変更
alter table <テーブル名> change column <変更前のカラム名> <変更後のカラム名> <データ型>;
-- カラムの順番修正(この方法でできないときは、カラム削除とカラム追加を組み合わせればいい)
alter table <テーブル名> modify <カラム名> <データ型> after <移動したい場所の上にあるカラム名>;
-- インデックス追加(unique制約をつけたい時はaddの後ろにuniqueをつける)
alter table <テーブル名> add index <インデックス名> (<カラム名1>, <カラム名2>, ...);
-- インデックス削除
alter table <テーブル名> drop index <インデックス名>;
-- string型のカラムにlimit付与(例はカラム追加時)
alter table <テーブル名> add column <カラム名> varchar(100);
--=> schema: t.string "カラム名", limit: 100
-- デフォルト値の追加
alter table <テーブル名> alter <カラム名> set default <デフォルト値>;
-- not nullの追加
alter table <テーブル名> modify <カラム名> <データ型> not null;
おわりに
最後まで読んでくださりありがとうございました!
私自身頭の中でイメージして考えることが苦手なので、このように書き出して整理してみることが有効であると考えています。メンバーの良いところを真似しつつ、自分に合った開発の仕方を模索していくことが大切であると日々感じています。
ご指摘等ございましたら、コメントなどで教えてください!