文章

Flask - 應用結構

目標

  • 理解為什麼需要模塊化結構
  • 使用藍圖 (Blueprint) 組織路由
  • 分離配置文件和其他模塊

步驟

  1. 為什麼需要模塊化?
    • 到目前為止,我們的代碼都在單一的 app.py 文件中。隨著功能增加,這會變得難以管理。
    • 模塊化結構可以將路由、模型和配置分開,類似於 MVC(模型-視圖-控制器)模式。
  2. 設置項目結構
    • 創建以下文件夾結構:
      1
      2
      3
      4
      5
      6
      7
      8
      
      flask_api/
      ├── app/
      │   ├── __init__.py
      │   ├── routes/
      │   │   ├── __init__.py
      │   │   └── items.py
      │   └── config.py
      └── run.py
      
    • 說明:
      • app/:應用主目錄。
      • routes/:存放路由模塊。
      • run.py:啟動應用。
  3. 配置應用
    • app/config.py
      1
      2
      
      class Config:
          DEBUG = True
      
    • app/__init__.py
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      
      from flask import Flask
      from .routes.items import items_bp  # 導入藍圖
      
      def create_app():
          app = Flask(__name__)
          app.config.from_object('app.config.Config')  # 載入配置
      
          # 註冊藍圖
          app.register_blueprint(items_bp, url_prefix='/api/v1')
      
          return app
      
    • run.py
      1
      2
      3
      4
      5
      6
      
      from app import create_app
      
      app = create_app()
      
      if __name__ == '__main__':
          app.run()
      
  4. 使用藍圖組織路由
    • app/routes/items.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
      
      from flask import Blueprint, jsonify, request
      
      items_bp = Blueprint('items', __name__)
      
      # 模擬數據庫
      items = [
          {'id': 1, 'name': 'Item 1', 'price': 10.0},
          {'id': 2, 'name': 'Item 2', 'price': 20.0}
      ]
      
      @items_bp.route('/items', methods=['GET'])
      def get_items():
          max_price = request.args.get('max_price', type=float)
          if max_price:
              filtered_items = [item for item in items if item['price'] <= max_price]
              return jsonify({'items': filtered_items})
          return jsonify({'items': items})
      
      @items_bp.route('/items', methods=['POST'])
      def create_item():
          if not request.is_json:
              return jsonify({'error': 'Request must be JSON'}), 400
          data = request.get_json()
          if 'name' not in data or 'price' not in data:
              return jsonify({'error': 'Missing name or price'}), 400
          new_item = {
              'id': len(items) + 1,
              'name': data['name'],
              'price': data['price']
          }
          items.append(new_item)
          return jsonify(new_item), 201
      
      @items_bp.route('/items/<int:item_id>', methods=['PUT'])
      def update_item(item_id):
          item = next((item for item in items if item['id'] == item_id), None)
          if not item:
              return jsonify({'error': 'Item not found'}), 404
          if not request.is_json:
              return jsonify({'error': 'Request must be JSON'}), 400
          data = request.get_json()
          item.update({k: v for k, v in data.items() if k in ['name', 'price']})
          return jsonify(item), 200
      
    • 代碼解釋
      • Blueprint('items', __name__):創建一個藍圖,命名為 ‘items’。
      • app.register_blueprint(items_bp, url_prefix='/api/v1'):將藍圖註冊到應用,並添加前綴 /api/v1
  5. 運行應用
    • 進入 flask_api/ 目錄,運行:
      1
      
      python run.py
      
    • 服務器啟動後,端點變為:
      • http://127.0.0.1:5000/api/v1/items
  6. 測試 API
    • 使用 Postman 測試:
      • GET /api/v1/items:獲取所有項目。
      • GET /api/v1/items?max_price=15:過濾價格低於 15 的項目。
      • POST /api/v1/items:創建新項目(JSON:{"name": "Item 3", "price": 30.0})。
      • PUT /api/v1/items/1:更新項目(JSON:{"price": 15.0})。
  7. 作業
    • 添加一個新藍圖 users,包含一個簡單的 GET 端點 /users,返回模擬的用戶列表。
    • config.py 中添加更多配置項,例如 SECRET_KEY,並在 create_app 中使用。

注意事項

  • 確保文件路徑正確,例如 from .routes.items import items_bp 中的點號表示相對導入。
  • 如果遇到導入錯誤,檢查 __init__.py 是否存在。

本文章以 CC BY 4.0 授權