文章

Flask - 環境變量與配置

目標

  • 使用 python-dotenv 管理環境變量
  • 配置開發、測試和生產環境
  • 確保敏感信息(如數據庫 URI)不硬編碼

步驟

  1. 準備環境

    • 繼續使用 flask_api/ 項目結構,激活虛擬環境:
      1
      2
      
      # Windows: flask_api_env\Scripts\activate
      # macOS/Linux: source flask_api_env/bin/activate
      
    • 安裝 python-dotenv
      1
      
      pip install python-dotenv
      
  2. 創建環境變量文件

    • 在項目根目錄下創建 .env 文件,存放敏感信息:
      1
      2
      3
      4
      
      # flask_api/.env
      FLASK_ENV=development
      SECRET_KEY=your-secret-key-here
      DATABASE_URL=sqlite:///todos.db
      
    • 注意.env 不應提交到版本控制(後續會添加到 .gitignore)。
  3. 更新配置

    • 修改 app/config.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
      
      import os
      from dotenv import load_dotenv
      
      # 載入 .env 文件
      load_dotenv()
      
      class Config:
          """基礎配置"""
          SECRET_KEY = os.getenv('SECRET_KEY', 'default-secret-key')
          SQLALCHEMY_TRACK_MODIFICATIONS = False
      
      class DevelopmentConfig(Config):
          """開發環境"""
          DEBUG = True
          SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URL', 'sqlite:///todos_dev.db')
      
      class TestingConfig(Config):
          """測試環境"""
          TESTING = True
          SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URL', 'sqlite:///todos_test.db')
          WTF_CSRF_ENABLED = False  # 禁用 CSRF(測試用)
      
      class ProductionConfig(Config):
          """生產環境"""
          DEBUG = False
          SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URL', 'sqlite:///todos_prod.db')
      
      # 環境映射
      config_map = {
          'development': DevelopmentConfig,
          'testing': TestingConfig,
          'production': ProductionConfig
      }
      
    • 代碼解釋

      • load_dotenv():從 .env 載入環境變量。
      • os.getenv():獲取環境變量,若無則使用默認值。
      • 三個配置類對應不同環境。
  4. 更新應用初始化

    • 修改 app/init.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
      
      from flask import Flask, jsonify
      from flask_sqlalchemy import SQLAlchemy
      from flask_marshmallow import Marshmallow
      from .routes.v1.todos import todos_bp as todos_v1_bp
      from .routes.v1.users import users_bp as users_v1_bp
      from .routes.v2.todos import todos_bp as todos_v2_bp
      from .config import config_map
      
      db = SQLAlchemy()
      ma = Marshmallow()
      
      def create_app():
          app = Flask(__name__)
      
          # 根據 FLASK_ENV 選擇配置,默認為 development
          env = os.getenv('FLASK_ENV', 'development')
          app.config.from_object(config_map[env])
      
          db.init_app(app)
          ma.init_app(app)
      
          app.register_blueprint(todos_v1_bp, url_prefix='/api/v1')
          app.register_blueprint(users_v1_bp, url_prefix='/api/v1')
          app.register_blueprint(todos_v2_bp, url_prefix='/api/v2')
      
          @app.errorhandler(404)
          def not_found(error):
              return jsonify({'error': 'Not Found', 'message': str(error)}), 404
      
          @app.errorhandler(400)
          def bad_request(error):
              return jsonify({'error': 'Bad Request', 'message': str(error)}), 400
      
          @app.errorhandler(500)
          def internal_error(error):
              return jsonify({'error': 'Internal Server Error', 'message': 'Something went wrong on our end'}), 500
      
          with app.app_context():
              db.create_all()
      
          return app
      
  5. 更新啟動腳本

    • 修改 run.py,支持環境變量啟動:

      1
      2
      3
      4
      5
      6
      
      from app import create_app
      
      app = create_app()
      
      if __name__ == '__main__':
          app.run(host='0.0.0.0', port=5000)  # 可選:允許外部訪問
      
  6. 測試不同環境

    • 開發環境
      • 確保 .envFLASK_ENV=development,運行:
        1
        
        python run.py
        
      • 檢查數據庫文件是否為 todos_dev.db
    • 生產環境
      • 修改 .envFLASK_ENV=production,或在命令行設置:
        1
        2
        3
        
        export FLASK_ENV=production  # macOS/Linux
        set FLASK_ENV=production     # Windows
        python run.py
        
      • 確認 DEBUG=False,數據庫為 todos_prod.db
    • 測試環境
      • 設置 FLASK_ENV=testing,運行並檢查 todos_test.db
  7. 保護敏感信息

    • 創建或更新 .gitignore,確保 .env 不被提交:
      1
      2
      3
      4
      5
      
      # flask_api/.gitignore
      *.db
      .env
      __pycache__/
      *.pyc
      
  8. 作業

    • .env 中添加一個自定義變量(例如 API_VERSION),並在應用中顯示當前版本(提示:可在路由中返回)。
    • 測試在沒有 .env 文件時,應用是否仍能使用默認配置運行。

測試 API

  • POST /api/v1/users
    • Body:{"username": "alice"}
    • 確認數據存入正確的數據庫文件。
  • GET /api/v2/todos
    • 確保功能正常,且環境切換不影響行為。

注意事項

  • 環境變量優先級:命令行 > .env > 代碼默認值。
  • 生產環境應使用更安全的數據庫(如 PostgreSQL),這裡僅用 SQLite 示範。

本文章以 CC BY 4.0 授權