React 結合 python API — 寫一個行事曆 App!使用 fastAPI + google sheet 當資料庫。
CRUD — 寫一個個人行事曆,前端用 React,後端用 python 的 fastAPI,資料庫使用 google sheet,並最後用 heroku 來佈署 python 檔。
✔ 前言
好無聊的話來寫一個個人行事曆吧(・ε・`)(?
本篇將結合前後端並佈署,實做一個能用的行事曆 App!(好像還沒看過有人這樣寫 CRUD 的教學文,恭喜終於不用再看 ToDo List )
將會學到
- 使用 React Calendar
- 使用 python fast API
- 使用 google sheet 當資料庫
- 佈署後端 heroku
- 佈署前端 netlify
開發環境
- ubuntu 20.04
- python 3.8.10
- node 16.13.2
✔ 目錄
▶ 前製作業 - github
▶ 開發 - 後端 python / google sheet
▶ 開發 - 前端 React peoject
▶ 佈署 - 佈署後端
▶ 佈署 - 佈署前端
▶ 總結
✔ 前製作業
建立 repo!
- 先在 github 新增一個 repo
- clone 到本機電腦資料夾
- 並在檔案中新增 frontend 資料夾、backend 資料夾
- 將目前的檔案推上 github ,依序下指令
$ git add .
$ git commit -m ‘init’
$ git push
(上述步驟可能會因為權限問題而推送失敗,如果有遇到問題可以再討論討論)
初始化專案資料夾
- 終端機進入 frontend 資料夾下指令
$ npx create-react-app .
- 終端機進入 backend 建立一個檔案
main.py
- 這時候 CRA 應該也跑完了,我們將 src 資料夾中不需要的檔案/程式碼去掉,如下圖:
✔ 開發
後端 python / google sheet
先從後端 python fastAPI 連上 google sheet 開始(完全在複製自己之前寫過的 文章 XD )
- 申請 google sheet api >新增專案
2. 在該專案中的資訊主頁,點選上方的「啟用 API 和服務」
3. 於搜尋框輸入「Google Sheets API」> 點選啟用
4. 進入憑證頁面,然後點上方「建立憑證」
5. 選「服務帳戶」
6. 輸入帳戶的詳細資料後按建立(二跟三可以不填)
7. 完成後下面的服務帳戶會多一個電子郵件>點進去電子郵件>服務帳戶詳細資料>複製電子郵件地址
8. 點上方的標籤「金鑰」,選擇「建立新的金鑰」
9. 選擇 JSON 後按建立,存到專案的資料夾中
10. 新增一張 google sheet 表,共用人員貼上剛剛複製的電子郵件,選擇為編輯者>傳送!
這樣連接 google sheet 的部份就完成了。
接著進到 backend 資料夾中
- 建立虛擬環境
$ python3 -m venv env
2. 進入虛擬環境 env
$ source env/bin/activate
3. 安裝套件
$ pip install fastapi
$ pip install "uvicorn[standard]"
$ pip install gspread oauth2client
$ pip install pydantic
4. 新增檔案 .gitignore,並在裡面輸入虛擬環境的資料夾名稱
env
5. 進入 backend/main.py
中,貼上以下的 code
import gspread
from oauth2client.service_account import ServiceAccountCredentials as SAC
Json = "剛剛存進來的json金鑰檔名.json"
Url = ["https://spreadsheets.google.com/feeds"]
Connect = SAC.from_json_keyfile_name(Json, Url)
GoogleSheets = gspread.authorize(Connect)
Sheet = GoogleSheets.open_by_key("表單網址d/後面的KEY")
Sheets = Sheet.sheet1datas = ["03/11", "15:00", "dinner", "taipei"]
// 先塞一筆假資料Sheets.append_row(dataTitle)
Sheets.append_row(datas)
print("寫入成功")
print(Sheets.get_all_values())
- 試著 run 看看
$ python main.py
成功!!
接著使用 fastAPI,把他變成一個可以呼叫的 URL
import gspread
from oauth2client.service_account import ServiceAccountCredentials as SAC
from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModelJson = "你的JSON檔名.json"
Url = ["https://spreadsheets.google.com/feeds"]
Connect = SAC.from_json_keyfile_name(Json, Url)
GoogleSheets = gspread.authorize(Connect)
Sheet = GoogleSheets.open_by_key("你的sheet KEY")
Sheets = Sheet.sheet1
# ----- fast api -----
app = FastAPI()@app.get("/")
def getAllData():
return Sheets.get_all_records()class Info(BaseModel):
id: int
data: list@app.post("/addNewEvents")
def getInformation(info: Info):
Sheets.append_row(info.data)
return {"status": "SUCCESS", "data": info}
- 啟動
$ uvicorn main:app --reload
在網址輸入 http://127.0.0.1:8000/,會看到我們之前存入 sheet 的資料
使用 fast API 提供的 doc 文件進行測試,也可以正常新增資料
後端的程式碼部份完成~
但!還不能推上 github,我們還需要將秘密檔(.json
)跟 key 另外處理
- 新增一個
key.py
sheetKey = '你的KEY'
2. 在 .gitignore
中新增兩行
env
calendar.json
key.py
好了~ 可以先推一版上 github (・ิω・ิ)!
前端 React peoject
處理前端的部份,進到 frontend 資料夾
- 在
src
底下,新增一個檔案PersonalCalendar.js
import React from 'react'const PersonalCalendar = () => {
return (
<div>PersonalCalendar</div>
)
}export default PersonalCalendar
2. 在App.js
中 import
- 看一下畫面正不正常
$ yarn start
然後…
我們試著 fetch 資料,會發現很多跨域的 error
Access to fetch at 'http://127.0.0.1:8000/' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
終於找到解答,跟著下面的步驟來解決它!
先回到後端(backend) main.py
中,把 call API 的 URL 加入設定,新增以下幾行:
from fastapi.middleware.cors import CORSMiddlewareorigins = [
"http://localhost",
"http://localhost:3000",
]app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ... 以下省略
接著回到 PersonalCalendar.js
中,進行 GET & POST 測試
終於成功(痛哭流涕(´• ω •`)ノ)
確定抓取資料沒問題後,就要開始將資料塞進行事曆中啦,先來測試套件的使用
- 在/frontend 中安裝套件
$ yarn install @fullcalendar/daygrid @fullcalendar/react
2. import 套件
import FullCalendar from "@fullcalendar/react";
import dayGridPlugin from "@fullcalendar/daygrid";
import "@fullcalendar/daygrid/main.css";
import "@fullcalendar/list/main.css";
3. 使用(超簡單)
<FullCalendar
events={events} // 所有事件
plugins={[dayGridPlugin]} // 行事曆長相
height='500px' // 樣式 可以不填
eventClick={function (arg) {
alert(arg.event.title)
alert(arg.event.start)
}} // 點擊事件要觸發的function
/>
- 可以複製以下的 code 跑跑看
有了樣式後,就可以把 API 取得的資料塞進去
- 先將 google sheet 的資料格式改為 FullCalendar 的格式
2. 使用 useState 管理變數
const [events, setEvents] = useState(initEvents)
// initEvents 為上面原本的 events
3. 新增 function
function getData() {
fetch(url, {
method: 'GET',
redirect: 'follow',
})
.then((response) => response.json())
.then((result) => {
console.log(result)
setEvents(result)
})
.catch((error) => console.log('error', error))
}
將江!資料成功顯示在行事曆上了(感動(´༎ຶД༎ຶ`)
最後一步,新增資料到 google sheet 中
- 安裝套件 uuid
$ yarn add uuid react-datetime moment
2. 新增 state
const [startTime, setStartTime] = useState('2022-03-19T17:30')
const [title, setTitle] = useState('')const newEvents = {
id: uuidv4(),
data: [uuidv4(), startTime, title],
}
3. 新增樣式及 onChange function
<Datetime
onChange={(date) => {
setStartTime(date._d)
}}
/>
title
<input
type='text'
name='title'
value={title}
onChange={(e) => {
setTitle(e.target.value)
}}
/>
4. 丟資料
<button
onClick={() => {
postData(url, newEvents)
}}
>
OK
</button>
完整的 code
✔ 佈署
佈署後端
在後端 /backend 資料夾中
- 新增檔案
runtime.txt
(指定 python 版本)
python-3.8.10
2. 新增檔案 Procfile
(給 heroku 看的檔案)
web: uvicorn main:app --host=0.0.0.0 --port=${PORT:-5000}
3. 將我們的 python API 佈署到 heroku 上面
3–1. 建立 heroku 帳號
3–2. Create new app
3–3. 接著在終端機,輸入以下指令,登入 heroku
$ heroku login
3–4. 點任一鍵登入(會跳出網頁按下 Login)
3-5. 在 heroku 的 deploy 頁面可以看到步驟,依序下指令
$ git init$ heroku git:remote -a xxx(你的app名稱)$ git add .$ git commit -am "make it better"$ git push heroku master
上述都沒問題的話就會開始佈署!(也可以在 heroku 頁面查看 log)
成功後我們會在 setting 的 Domains 中看到一串 URL
點開他會發現是 Error (果然太順利肯定有問題 (・ิω・ิ)
在終端機輸入 $ heroku logs -- tail
,就會發現是因為我們將 key
跟 json
檔案隱藏了,Git 抓不到值導致的,所以把這兩個檔案從 .gitignore
移除是最快的方法。
但是!身為一個不專業工程濕,我們是不能把 key 隨便 push 到雲端,這時候就要借用 heroku 的 Config Vars 功能。
在 settings 頁面可以看到 Config Vars
- 將
key.py
中的 sheetKey 輸入進去,並在我們的 backend 資料夾中新增.env
檔
sheetKey = 'xxx'
2. .gitignore
會調整為
3. 測試一下
- 在終端機輸入
heruko local
,會 run 在本機的 0.0.0.0:5000
ㄛ耶成功 (๑´ㅁ`)
- 將異動的部份推上 heroku,依序輸入下面的指令
$ git add.
$ git commit -m 'init'
$ git push heroku master
再回到我們佈署的 URL,可以看到資料都跟我們 google sheet 中的一樣,佈署後端的部份就到這邊結束了。
接著就是來測試 POST 的功能,啟動我們的前端專案 yarn start
PersonalCalendar.js
中的 url 改為上面佈署的網址
let url = 'http://127.0.0.1:8000'
=>
let url = 'https://xxxxx.herokuapp.com'
測試一下 post 的功能
佈署前端
終於前端的部份也完成,剩下佈署前端的部份
- 在前端 /frontend 資料夾下 Build 指令
$ yarn build
2. 打開 https://app.netlify.com/ 把打包完成的 /build 整包拉進去
3. 等它跑完
4. 在 site detail 中可以修改 URL 的名稱
進去自己設定的網址,就可以正式使用它啦 (๑´ㅁ`)!
✔ 總結
這篇也是寫了好幾天,寫到一半都會覺得,偏實做的文章都沒什麼人要看,但跟著這篇做(應該或許可能…)可以學到很多東西,有任何問題也可以一起討論。
個人在處理 CORS 的部份研究了一下,資訊程式領域真的好深rrr,希望自己藉由寫文章能慢慢的持續前進 ٩(ˊᗜˋ )و!