0%

前言

繼前兩篇我們成功在本地端為我們的 flask api 串接資料庫
現在我們要在 Heroku 上做一樣的事情
恩… 應該說目標一樣
但要做的設定還蠻不一樣的
繼續往下看吧!

在 Heroku 串接 API 和 Postgres

首先我們需要在 Heroku 上啟用一個外掛
叫 “Heroku Postgres”
你可以開瀏覽器到 Dashboard
Resources > Add-ons > Heruku Postgres

接下來會詢問你要用什麼方案
稍微看了一下發現居然有每個月 16,000 美元的方案OAO
好扯喔
記得在查資料的過程中看到有人說
「也許 Heroku 算不上便宜 (或許可以說它是最貴的)」
不過幸好我們的用量不高
TAN 也根本沒人要看吧! (誤
我們就選免費的ㄅ

以上這些步驟可以用一行命令完成:

heroku addons:create heroku-postgresql:hobby-dev --app <heroku_app_name>

用 –app 這個 flag 可以指定 app 名稱
如果你在 heroku 上有多個 app,指定一下比較不會出錯

再來你需要一組 uri 作為 SQLALCHEMY_DATABASE_URI 的設定值,執行
heroku config --app <heroku_app_name>
他就會給你一長串postgres://.........的 uri,但直接使用這組 uri 會出錯
我們稍微把它改一下,變成postgresql://........,這樣就可以了

打開app.py,把之前設定 SQLALCHEMY_DATABASE_URI 的部分改成

1
2
3
4
5
6
7
8
9
ENV = 'prod'
if ENV == "dev":
app.debug = True
app.config["SQLALCHEMY_DATABASE_URI"] = 'postgresql://postgres:et0997@localhost/TAN_test_data'
else:
app.debug = False
app.config["SQLALCHEMY_DATABASE_URI"] = 'postgresql://.........'

app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

這邊我令了一個 ENV 變數判斷現在是開發階段還是成品階段
如要回到本地測試,只要把 ENV 設為 dev 即可

然後我也把 debug 的設定放到這段的 if statement 中
所以程式中原本的app.run(debug=True)也可以改回app.run()

在 Heroku 建立 table

先確定你有依照上一篇的教學將專案 deploy 到 heroku 上
執行 heroku run python 後輸入指令

1
2
from app import db # 從 app.py 中引入 db 模組
db.create_all() # 在 app.config 中所指定的 database 中建立 table

如果要刪除 table 就用db.drop_all()

完成,之後就能夠用 ORM 指令操作 database 了!

在 heroku 上用 psql 檢視資料

如果想看 heroku 上的資料,執行
heroku pg:psql
就會進到 psql 的 shell 中

\list
這個指令能印出 post gres 中的所有資料庫
不過如果在 heroku postgres 上執行的話,他只會跳出一堆以亂數取名的資料庫,對我們來說其實沒什麼意義
但如果在本地端開 psql 的話,你看到的就會是你之前創立過的資料庫了
要切換所在資料庫,執行 \c <資料庫名稱>

\dt
這個指令會印出目前所在的資料庫中有哪些 table

接下來你就能用 SQL 來獲取資料
假設資料表名稱是 table_issues
SELECT * FROM table_issues

結語

這是我第一次架設 api
回頭一看發現學了好多東西
Flask, Flask-SQLAlchemy, Heroku, PostgreSQL
完全沒料到,原本以為只要學 Flask 就好 XD

不過也好啦,直接學了整個體系,包含api功能,架設到雲端,資料庫的存取
要不是我已經有了想達成的目標(優化TAN網站)
不然如果我只是單純想學做 API 的話可能只會學到 Flask 而已
之後的某月某日還是會需要惡補其他環節的

前言

寫到這才發現我之前漏了 heroku 的介紹…
趕快補一下

Heroku 設定

heroku 是一個雲端平台,意思是他提供我們一個在網路上的空間,可以存放資料
更重要的是,你可以利用它部屬網站、API,這樣你就能用網址連線到你的網站了

有一個在 heroku 不斷出現的詞彙叫做 Dyno
Dyno 是運行和響應請求的應用程序的實例,欲執行網站就至少要一個 Dyno
以我目前的理解,可以把它當作 heroku 版的 repo

在免費方案下,

  1. Dyno 有 550 小時/月的免費時數,若通過信用卡驗證,額外贈送 450 小時/月,總共免費時數為 1,000 小時/月,若多個 Dyno 執行則可分攤掉時數
  2. Dyno 在 30 分鐘內沒有任何流量,系統自動進入睡眠狀態,睡眠狀態下則不消耗時數
  3. 睡眠狀態下 Dyno 收到請求,等喚醒約20秒啟動系統
  4. 儲存空間為 512MB

我已經在 Heroku 上建立帳號,建立新 app 叫做 taiwan-astronomy-network-api
然後便可繼續以下的步驟

一些設定檔

三份檔案要建立
runtime.txt 放的是這份專案所使用的 python 版本

1
python-3.9.6

requirements.txt 放的是這份專案所需要的模組
執行 pip freeze > requirements.txt 即可

Procfile 是heroku運行所需的檔案,注意沒有副檔名,P要大寫

1
web gunicorn app:app

第一個 app 對應的是 app.py 檔名的 app
第二個 app 對應的是 app.py 中 app = Flask(__name__) 的這個 app

這三個檔案稍有不對就會出錯
所以在繼續往下走前一定要再三確認

部屬專案

先到這裡安裝 heroku-cli

接著部屬專案的步驟如下:

heroku login

heroku git:remote -a <app名稱>

1
2
3
$ git add .
$ git commit -am "make it better"
$ git push heroku master

這邊不一定要用 master 要看你本地 repo 是在什麼 branch 上

到這邊應該沒問題了,趕緊下一篇就來串接資料庫!

前言

今天這篇我要來記錄如何在 Flask API 中串接並使用 Postgres
我在上一篇中提到在 Heroku 上用 SQLite 作為資料庫的缺點,以及 Heroku 推薦使用 Postgres
因此我去研究了如何做到這一點
過程遇到很多 error 真的很崩潰,也沒有把遇到的每個問題都記錄下來有點可惜
現在就憑印象,能寫多少就多少吧

這邊我會把 postgres 操作分成 local 跟 remote
先從 local 開始吧

安裝 Postgres

PostgreSQL 是一款蠻有名的關係資料庫
有時候縮寫成 Postgres
那我們到這裡下載不同作業系統對應的安裝軟體
那我用的是 Windows,所以這裡就用 windows 來舉例
安裝時注意一件事
我們要另外自己手動安裝 pgAdmin
所以記得把這個 pgAdmin4 的選項取消,不要選

接下來他會請你指定安裝路徑,super 使用者的密碼,預設 port,時區(選default就好)

再來要到這裡安裝 pgAdmin
安裝過程很單純,一直下一步到安裝就好,不會有什麼問題
完成後打開它

到這邊安裝就差不多了

在本地串接 API 和 Postgres

點擊此處建立資料庫,取名
(以後面的例子而言我是取 issuedata,不是圖裡的 NewDatabase)

接下來回到程式端
前幾篇文章的程式碼都是 for 教學用的
但我現在做了正式版,是將來要應用在 TAN 網站的版本
為了不造成混淆我先把連接資料庫前的正式版程式碼貼上來~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
from flask import Flask, Request
from flask_sqlalchemy import SQLAlchemy
from flask_cors import CORS

app = Flask(__name__)
CORS(app)

db = SQLAlchemy(app)

class Issue(db.Model):
__tablename__ = "issue_table"
id = db.Column(db.Integer, primary_key=True)
year = db.Column(db.Integer, nullable=False)
month = db.Column(db.Integer, nullable=False)
date = db.Column(db.Integer, nullable=False)
title = db.Column(db.String(100), nullable=False)
content = db.Column(db.String(2000))

def __repr__(self):
return f"{self.year}/{self.month}/{self.date} - {self.title}"


@app.route('/')
def index():
return 'Wellcome to TAN API!'


@app.route('/archive')
def get_archive():
issues = Issue.query.all()
output = []
for issue in issues:
issue_data = {
"id": issue.id,
"year": issue.year,
"month": issue.month,
"date": issue.date,
"title": issue.title,
"content": issue.content,
}
output.append(issue_data)

return {"drinks": output}


if __name__ == "__main__":
app.run()

要連接本地 postgres 資料庫,我需要加上:

1
2
app.config["SQLALCHEMY_DATABASE_URI"] = 'postgresql://postgres:et0997@localhost/issuedata'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

我們先來看第一行

app.config["SQLALCHEMY_DATABASE_URI"] = 'postgresql://postgres:et0997@localhost/issuedata'

這段 uri 的格式是 postgresql://<username>:<user_password>@<port>/<database_name>

  • username: PostgreSQL 的使用者。由於我沒有另外新建使用者,所以就用 super user 也就是postgres就可以了
  • user_password: user 密碼。你在安裝 postgres 的時候不是有設定過一次密碼嗎? 就是那個(user 選 postgres 的話啦)
  • port: 連接端口。由於我們是在本地測試,所以填localhost就好
  • database_name: 你剛剛用 pgAdmin 建立的那個資料庫名稱

總之這行是在告訴 Flask_SQLAlchemy 應該連接什麼資料庫,要怎麼找到它

最底部的 app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
是為了避免跳出以下 warning

1
2
warnings.warn('SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead 
and will be disabled by default in the future. Set it to True to suppress this warning.')

就我的理解
這是 Flask_SQLAlchemy 造成的小問題
它的意思是
由於 Flask_SQLAlchemy 是 SQLAlchemy 的一個包裝(類似升級版)
本質上還是 SQLAlchemy 在運作
而 Flask_SQLAlchemy 有自己的一套 event notification system
(不知道怎麼翻比較好,直接用原文)
所以它必須隨時監聽 SQLAlchemy 的任何修改(如’SQLALCHEMY_TRACK_MODIFICATIONS’字面上的意思)
這可能會造成性能瓶頸
設為 False 避免這個問題

至此就設定好本地連接了

建立 local DB 的 table 和 資料

接著打開 python shell

1
2
3
4
5
6
from app import db, Issue # 從 app.py 引入 db 和 Issue 模組
db.create_all() # 在 database 中建立 Table,名稱為 Issue (如果沒用 __tablename__ 更改預設名稱的話)
# 宣告新的單筆資料 issue
issue = Issue(year=2077,month=1,date=27,title="Issue Title", content="This is something important.")
db.session.add(issue) # 把 issue 加進 session
db.session.commit() # 提交 session

完成後打開 pgAdmin
從以下路徑找到名為 issuedata 的 table

耶! 看到資料了

結語

剛剛的安裝過程都只是在自己的電腦上存取資料而已
但最終我們要把 API 放在網路上呀
到時要怎麼用 Postgres 呢?

我覺得 部落格好像不太適合寫太長
遠端的部分就留到下一篇吧!