
NotionのCSV取込みでリレーション張れないからGASで一括設定した
Notionってデータベースを直感的に作成できて、それが簡単に様々なレイアウト表示できるという利点が魅力ですよね。
異なるテーブルを、多対多のリレーションできるのも素敵じゃないですか。
で、CSVで複数データを一括登録するとき、リレーションもできるでしょ!という頭で取り込んだら、張れていないわけですね。ショック🤯
仕組みを理解すれば、まあ難しいよねというのは理解できるんですが、手作業で全部設定するのはしんどいよ・・と思って、GAS(Google Apps Script)で一括リレーション設定にトライしてみた話です。
茶番含めて時系列で書いていきますので、GASのコードだけ見たい!という方は、目次の「コード.gsのサンプルコード」からどうぞ。
やりたかった完成図

「食材一覧」と「悩み別レシピ」という2つのテーブルがあります。この二つを関連付けしました。具体的な使用例:
🍅 食材一覧
スーパーにて「アボカドが安い!何に効くんだっけ。いいレシピあれば追加食材も一緒に買っておきたいな」
😷 悩み別レシピ
「風邪引きそう!家にある食材で薬膳できないかなあ」
こんなかんじで、目次と索引みたいな感じのテーブルを目指しました。
実際に作ってみよう
手元にいろいろ散らかったレシピ集があるので、CSVに整理して一括登録することにしました。
1. 2つのテーブルを作成

🍅 食材一覧
食材名|タイトル
分類|セレクト
😷 悩み別レシピ|リレーション|対象:😷 悩み別レシピ
※プロパティ設定で「悩み別レシピに表示」をオンにすると、「悩み別レシピ」データベースにもリレーション項目が自動追加される
効能|ロールアップ|リレーション:😷 悩み別レシピ|プロパティ:悩み|計算:オリジナルを表示する
😷 悩み別レシピ
レシピ名|タイトル
悩み|セレクト
🍅 食材一覧|リレーション|対象:🍅 食材一覧
2. 取り込むCSVを用意

3. まずは食材一覧をCSV取り込み



ならば・・

そこから・・

オプション化されて一安心。
4. 悩み別レシピをCSV取り込み



🙃インポートでリレーションは張れない
■ Notion よくあるご質問(FAQ)
❓ リレーションをエクスポートやインポートできますか?
リレーションを含むデータベースをCSVファイルとしてエクスポートすると、リレーションプロパティは、プレーンテキストでURLとしてエクスポートされます。現時点では、そのCSVをNotionに再インポートすることで他のデータベースとのリレーションを再構築することはできません。
以下リンクも参考までに貼り付け。Notion APIでリレーション設定はできるらしいけど、あくまでデータベースのプロパティに、紐づけたい対象データベースを設定するだけっぽい。私がやりたいのは、プロパティ(項目)ではなく行の一括登録なの・・
5. GASで一括設定してやろうじゃないか
いったん、手順4で行った2つ目のデータベース(悩み別レシピ)のCSV取込み直後の状態に戻します。


あと、リレーションする項目と名前が被らないよう「食材一覧」→「食材」へ変更しておく
6. 必要な設定・キー等を取得しておく
Notionインテグレーションを作成・シークレットキーをコピーしておく
各データベースに、上記1のインテグレーションをコネクト追加する
各データベースのIDをコピーしておく
上記の具体的な方法は、以下の記事をご参照ください。
目次の手順①と手順②が参考になるかなと思います😀
7. Google Apps Script(GAS)にコード貼り付け

コード.gsのサンプルコード
const NOTION_API_KEY = 'インテグレーションのシークレットを貼り付けてください'; // 【インテグレーションシークレット】
const FOODS_DATABASE_ID = '1つ目のデータベースIDを貼り付けてください'; // 【食材一覧DB】
const WORRIES_DATABASE_ID = '2つ目のデータベースIDを貼り付けてください'; // 【悩み別レシピDB】
/**
* メイン処理
*/
function setRelation() {
// 全食材取得
const foods = getFoods();
// 全悩み取得
const worries = getWorries();
// 各悩み別レシピごとに食材をリレーション
worries.forEach(worry => {
const foodPageIds = [];
worry.foodItems.forEach(itemName => {
// リレーション先となる食材ページIDを取得
let foodPageId = getFoodPageId(foods, itemName);
if (foodPageId) {
foodPageIds.push({ id: foodPageId });
}
});
// 更新
if (foodPageIds.length !== 0) {
updateRelation(worry.pageId, foodPageIds);
}
});
}
/**
* 「食材一覧」DBの食材取得
*/
function getFoods() {
const url = `https://api.notion.com/v1/databases/${FOODS_DATABASE_ID}/query`;
const options = {
method: 'POST',
headers: {
'Authorization': `Bearer ${NOTION_API_KEY}`,
'Content-Type': 'application/json',
"Notion-Version": "2022-06-28",
}
};
const response = UrlFetchApp.fetch(url, options);
const data = JSON.parse(response.getContentText());
const foodDatas = data.results.map(food => {
return {
pageId: food.id,
name: food.properties['食材名'].title[0].text.content,
};
});
return foodDatas;
}
/**
* 「悩み別レシピ」DBの食材取得
*/
function getWorries() {
const url = `https://api.notion.com/v1/databases/${WORRIES_DATABASE_ID}/query`;
const options = {
method: 'POST',
headers: {
'Authorization': `Bearer ${NOTION_API_KEY}`,
'Content-Type': 'application/json',
"Notion-Version": "2022-06-28",
}
};
const response = UrlFetchApp.fetch(url, options);
const data = JSON.parse(response.getContentText());
const worriesData = data.results.map(worry => {
const foodItems = worry.properties['食材'].multi_select.map(item => item.name);
return {
pageId: worry.id,
foodItems: foodItems
};
});
return worriesData;
}
/**
* 悩み別レシピDBの項目「食材」から
* 食材一覧DBの食材ページIDを取得
*/
function getFoodPageId(foods, itemName) {
const matchingFood = foods.find(food => food.name === itemName);
if (matchingFood) {
return matchingFood.pageId;
}
}
/**
* 更新
*/
function updateRelation(worryPageId, foodPageIds) {
const url = `https://api.notion.com/v1/pages/${worryPageId}`;
const options = {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${NOTION_API_KEY}`,
'Content-Type': 'application/json',
"Notion-Version": "2022-06-28",
},
payload: JSON.stringify({
properties: {
"🍅 食材一覧": { // アイコンがあるならアイコンやスペースも忘れずに
relation: foodPageIds
},
},
}),
};
UrlFetchApp.fetch(url, options);
}
コード.gsに貼り付けたら、ツールバーの実行ボタンを押す。
8. 完成!

「食材一覧」データベースに設定したロールアップ(「効能」項目)も連動反映されました
おわりに
一度GASでこういう処理をつくる作業をやってみると、自分であれこれ試行錯誤できて、本体機能で実現できないことも実現できちゃうのが面白いところです🥰
ちなみにNotion APIを使うにあたり、APIプラットフォームの『Postman』がテスト用に便利です。データ構造も確認できるし、🔗Notion APIコレクション(データ取得・更新・追加などの設定がテンプレ化されたもの)も用意されています。
どこかのタイミングで紹介出来たらな~と思いつつ、以下の記事も丁寧に説明されているので、まずは参考先として貼らせていただきます🙇♀️