はじめに
アプリ開発中、複数テーブルにまたがるデータ削除で困ったことがありました。
問題
設定していたテーブルはこちらです。
books
| Name | Type | Option |
|---|---|---|
| id | int8 | |
| firebase_uid | varchar | non null |
| title | varchar | non null |
learnings
| Name | Type | Option |
|---|---|---|
| id | int8 | |
| book_id | int8 | non null |
| content | varchar | non null |
actions
| Name | Type | Option |
|---|---|---|
| id | int8 | |
| learning_id | int8 | |
| content | varchar | non null |
| frequency | varchar | non-null |
外部キーとしてbooks.id = learnings.book_idとlearnings.book_id = actions.learning_idとしていました。
複数テーブルの情報を削除する仕様になったさい、一つずつ削除の記載を行っていました。
Book
import { supabase } from "../utils/supabase";
const handleDelete = useCallback(async (bookId: number) => {
const deleteActionsData = await supabase.from("actions").delete().eq("learning_id", bookId);
if(deleteActionsData.error) {
console.error("Error deleting data:", deleteActionsData.error);
return;
}
const deleteLearningData = await supabase.from("learnings").delete().eq("book_id", bookId);
if(deleteLearningData.error) {
console.error("Error deleting data:", deleteLearningData.error);
return;
}
const deleteBookData = await supabase.from("books").delete().eq("id", bookId);
if (deleteBookData.error) {
console.error("Error deleting data:", deleteBookData.error);
return;
}
setBooks((prevBooks) => prevBooks.filter((book) => book.id !== bookId));
},[navigate])
しかし、この場合、2つ削除されても、1つ削除されない場合はDB整合性が合わなくなります。
また、コードが長くなるため短くしたい方向性で考えていました。
解決方法
supabaseでON DELETE CASCADEと変更します。
ALTER TABLE learnings
DROP CONSTRAINT learnings_book_id_fkey;
ALTER TABLE learnings
ADD CONSTRAINT learnings_book_id_fkey
FOREIGN KEY (book_id)
REFERENCES books(id)
ON DELETE CASCADE;
注意することは、CASCADEの設定は子テーブルに設定することです。
今回だと親がbooksで子がlearningsとなります。
books削除すればほかも削除されるようにコードを記載するようにしました。
Book
import { supabase } from "../utils/supabase";
const handleDelete = useCallback(async (bookId: number) => {
const deleteBookData = await supabase.from("books").delete().eq("id", bookId);
if (deleteBookData.error) {
console.error("Error deleting data:", deleteBookData.error);
return;
}
setBooks((prevBooks) => prevBooks.filter((book) => book.id !== bookId));
},[navigate])
おわりに
以前Djangoで使っていましたが、そのときのことを思い出しました。
