Flask - SQLAlchemy 數據庫操作
目標
- 實現基本的數據庫 CRUD 操作
- 學習 SQLAlchemy 的查詢語法
- 增強待辦事項 API 的功能
步驟
準備環境
- 繼續使用第 8 天的
flask_api/
項目結構,確保虛擬環境已激活:1 2
# Windows: flask_api_env\Scripts\activate # macOS/Linux: source flask_api_env/bin/activate
- 繼續使用第 8 天的
更新模型
修改 app/models.py,為
Todo
模型添加更多字段以支持豐富的查詢:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
from . import db from datetime import datetime class Todo(db.Model): __tablename__ = 'todos' id = db.Column(db.Integer, primary_key=True) title = db.Column(db.String(100), nullable=False) completed = db.Column(db.Boolean, default=False) created_at = db.Column(db.DateTime, default=datetime.utcnow) description = db.Column(db.String(255), nullable=True) # 新增描述字段 def to_dict(self): return { 'id': self.id, 'title': self.title, 'completed': self.completed, 'created_at': self.created_at.isoformat(), 'description': self.description }
注意:由於表結構改變,需刪除現有的
todos.db
文件並重新運行應用以重建表。
增強路由功能
修改 app/routes/todos.py,實現更複雜的數據庫操作:
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 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
from flask import Blueprint, jsonify, request, abort from ..models import Todo from .. import db todos_bp = Blueprint('todos', __name__) # GET - 獲取所有任務(支持過濾和排序) @todos_bp.route('/todos', methods=['GET']) def get_todos(): completed = request.args.get('completed', type=lambda x: x.lower() == 'true') search = request.args.get('search') # 搜索標題或描述 sort = request.args.get('sort', 'id') # 排序字段:id 或 created_at query = Todo.query if completed is not None: query = query.filter_by(completed=completed) if search: query = query.filter( db.or_(Todo.title.ilike(f'%{search}%'), Todo.description.ilike(f'%{search}%')) ) if sort == 'created_at': query = query.order_by(Todo.created_at.desc()) else: query = query.order_by(Todo.id.asc()) todos = query.all() return jsonify({'todos': [todo.to_dict() for todo in todos]}) # GET - 獲取單個任務 @todos_bp.route('/todos/<int:todo_id>', methods=['GET']) def get_todo(todo_id): todo = Todo.query.get_or_404(todo_id, description='Todo not found') return jsonify(todo.to_dict()) # POST - 創建新任務 @todos_bp.route('/todos', methods=['POST']) def create_todo(): if not request.is_json: abort(400, description='Request must be JSON') data = request.get_json() if 'title' not in data: abort(400, description='Missing title') todo = Todo( title=data['title'], completed=data.get('completed', False), description=data.get('description') ) db.session.add(todo) db.session.commit() return jsonify(todo.to_dict()), 201 # PUT - 更新任務 @todos_bp.route('/todos/<int:todo_id>', methods=['PUT']) def update_todo(todo_id): todo = Todo.query.get_or_404(todo_id, description='Todo not found') if not request.is_json: abort(400, description='Request must be JSON') data = request.get_json() if 'title' in data: todo.title = data['title'] if 'completed' in data and isinstance(data['completed'], bool): todo.completed = data['completed'] if 'description' in data: todo.description = data['description'] db.session.commit() return jsonify(todo.to_dict()), 200 # DELETE - 刪除任務 @todos_bp.route('/todos/<int:todo_id>', methods=['DELETE']) def delete_todo(todo_id): todo = Todo.query.get_or_404(todo_id, description='Todo not found') db.session.delete(todo) db.session.commit() return jsonify({'message': 'Todo deleted'}), 200
代碼解釋:
filter_by
:精確匹配(例如completed=True
)。ilike
:不區分大小写的模糊匹配,用於搜索。db.or_
:組合多個條件。order_by
:按指定字段排序,desc()
表示降序。
運行應用
- 刪除舊的
todos.db
(如果存在),然後運行:1
python run.py
- 這會重新創建數據庫表。
- 刪除舊的
測試 API
- 使用 Postman 測試:
- POST /api/v1/todos(創建數據):
- Body:
{"title": "Learn SQLAlchemy", "description": "Study database operations", "completed": false}
- 預期響應:
{"id": 1, "title": "Learn SQLAlchemy", "completed": false, "created_at": "...", "description": "Study database operations"}
- Body:
- POST /api/v1/todos(再添加一條):
- Body:
{"title": "Build API", "description": "Create a Flask API", "completed": true}
- Body:
- GET /api/v1/todos?completed=true:
- 預期響應:包含
completed
為 true 的任務。
- 預期響應:包含
- GET /api/v1/todos?search=flask:
- 預期響應:包含 “flask” 在標題或描述中的任務。
- GET /api/v1/todos?sort=created_at:
- 預期響應:按創建時間降序排列的任務列表。
- PUT /api/v1/todos/1:
- Body:
{"description": "Updated description"}
- 預期響應:更新後的任務。
- Body:
- DELETE /api/v1/todos/2:
- 預期響應:
{"message": "Todo deleted"}
- 預期響應:
- POST /api/v1/todos(創建數據):
- 使用 Postman 測試:
作業
- 添加一個批量刪除端點
DELETE /api/v1/todos
,接受 JSON 數組(例如{"ids": [1, 2]}
)並刪除多個任務。 - 在 GET
/todos
中添加分頁功能(提示:使用limit
和offset
)。
- 添加一個批量刪除端點
注意事項
- 如果表結構改變(如新增字段),需重建數據庫(刪除
todos.db
並重新運行)。 ilike
需要字符串兩側加%
,用於模糊查詢。
本文章以 CC BY 4.0 授權