iT邦幫忙

2025 iThome 鐵人賽

DAY 23
0
AI & Data

AI 營養師 + Web3 數位健康護照系列 第 23

Day23. Flask 與資料庫整合 Ep2:練習用 SQLite 實作留言板功能(&單元測試)

  • 分享至 

  • xImage
  •  

今天就用「SQLite」做個小練習,順便實作一次「單元測試」

剛開始寫 Flask 專案時,常常都會擔心:「要是到上線前才出現 bug 怎麼辦?到時候會不會要全部重做?」其實只要加上一點單元測試,就能降低很多錯誤出現的機率

單元測試的重要性:可參考 Day12. 單元測試 (Unit Test),讓每個功能安心上路。

1. 專案結構規劃

flask_sqlalchemy_demo/
│
├── app.py               # Flask 主程式
├── models.py            # 資料庫模型
├── tests/               # 單元測試在這裡呦!!
│   └── test_models.py   # 用來檢查資料模型的正確性
├── templates/
│   ├── index.html       # 顯示留言板
│   └── add_comment.html # 新增留言頁面
└── requirements.txt     # 套件

2. 環境建置:安裝必要套件

為了讓開發之路更加順暢,不妨在一開始就建立虛擬環境,可以省去日後許多環境交錯的困惱。

python -m venv venv
source venv/bin/activate       # Linux / Mac  
source venv/Scripts/activate   # Windows  

# 安裝必要套件
pip install flask flask_sqlalchemy pytest

3. 初始化 Flask 應用與資料庫(app.py)

from flask import Flask, render_template, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
import os

app = Flask(__name__)

# 定義資料庫路徑與配置
base_dir = os.path.abspath(os.path.dirname(__file__))
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(base_dir, 'data.sqlite')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

# 建立 SQLAlchemy 物件
db = SQLAlchemy(app)

# 匯入模型
from models import Comment

@app.route('/')
def index():
    comments = Comment.query.order_by(Comment.timestamp.desc()).all()
    return render_template('index.html', comments=comments)

@app.route('/add', methods=['GET', 'POST'])
def add_comment():
    if request.method == 'POST':
        content = request.form.get('content')
        if content:
            new_comment = Comment(content=content)
            db.session.add(new_comment)
            db.session.commit()
            return redirect(url_for('index'))
    return render_template('add_comment.html')

if __name__ == '__main__':
    with app.app_context():
        db.create_all()  # 建立資料庫與表格
    app.run(debug=True)

4. 建立資料模型(models.py)

from app import db
from datetime import datetime

class Comment(db.Model):

    # 綁定的資料表名稱
    __tablename__ = 'comments'  

    # 主key
    id = db.Column(db.Integer, primary_key=True)  
    
    # 留言內容
    content = db.Column(db.String(255), nullable=False)  
    
    # 留言時間
    timestamp = db.Column(db.DateTime, default=datetime)  

    def __repr__(self):
        return f'<Comment {self.content[:20]}>'

5. 單元測試

有了基本功能後,別忘記替 models 檔案安排個小小測試,請在 tests/test_models.py 新增以下程式碼:

import pytest
from app import db
from models import Comment

# 測試函式都以 test_ 開頭喔~~
def test_comment_create():
    comment = Comment(content="單元測試留言")
    
    # assert 的任務是:負責檢查結果正確與否
    assert comment.content == "單元測試留言"
    assert isinstance(comment.timestamp, type(comment.timestamp))

6. 執行單元測試

在專案根目錄,輸入:pytest
pytest 會自動執行一次小測試,檢查可能會出問題的地方。

  • 測試未通過:
    https://ithelp.ithome.com.tw/upload/images/20251006/20129220hMKRgGDW8z.jpg

  • 測試通過:
    https://ithelp.ithome.com.tw/upload/images/20251006/20129220uyC7qSM9rX.jpg

7. 編輯主要留言板頁面(templates/index.html)

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>留言板</title>
</head>
<body>
  <h1>留言板</h1>
  <a href="{{ url_for('add_comment') }}">新增留言</a>
  <ul>
    {% for comment in comments %}
      <li>{{ comment.content }} <small>({{ comment.timestamp.strftime('%Y-%m-%d %H:%M') }})</small></li>
    {% else %}
      <li>目前沒有留言</li>
    {% endfor %}
  </ul>
</body>
</html>

8. 新增留言頁面(templates/add_comment.html)

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>新增留言</title>
</head>
<body>
  <h1>新增留言</h1>
  <form action="{{ url_for('add_comment') }}" method="post">
    <textarea name="content" rows="4" cols="50" placeholder="請輸入留言"></textarea><br>
    <input type="submit" value="送出">
  </form>
  <a href="{{ url_for('index') }}">回留言板</a>
</body>
</html>

9. 執行 app

  • CRUD 昨天已經有說過,這裡就不再贅述囉!!

Ref.


上一篇
Day22. Flask 與資料庫整合 Ep1:Flask、SQLite 與 SQLAlchemy「夠用就好」的佛系開發哲學
下一篇
Day24. Flask 與資料庫整合 Ep3:用 TDD(測試驅動開發)+ SQLite 完成AI營養顧問的資料庫功能 Part-1
系列文
AI 營養師 + Web3 數位健康護照27
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言