iT邦幫忙

2023 iThome 鐵人賽

DAY 0
0
自我挑戰組

從零到全端:轉職者的 To-Do List 技能之旅系列 第 7

從零到全端:轉職者的 To-Do List 技能之旅-後端開發- Flask-SQLAlchemy

  • 分享至 

  • xImage
  •  

今日目標

  1. 設定 Flask-SQLAlchemy 的連線
  2. 建立 Member、Task 模型(db.model)
  3. 嘗試使用 Flask 操控 MySQL

設定 Flask-SQLAlchemy

Flask-SQLAlchemy

一個讓 Flask 能夠操控資料庫的套件,並且可以選擇使用操控物件的方法來與資料庫互動(ORM)
簡單說就是:
在 Flask 中可以用熟悉的物件方式去操控資料庫
例如:

Task.query.all()   ==>   SELECT * FROM member;

/images/emoticon/emoticon12.gif
推薦資源:
Flask-SQLAlchemy — Flask-SQLAlchemy Documentation (3.1.x)
[Flask教學] Flask-SQLAlchemy 資料庫連線&設定入門(一) - Max行銷誌
Flask 学习-12.Flask-SQLAlchemy 连接 mysql 数据库 - 上海-悠悠 - 博客园

設定

建立實例:

  • 我們在 extensions.py 中已經有建立了 SQLAlchemy 的 db 實例
    from flask_sqlalchemy import SQLAlchemy
    db = SQLAlchemy() 
    

連結資料庫:

  • 在 config.py 中設定了 SQLALCHEMY_DATABASE_URI
  • 設定的規則:dialect + driver://username:passwor@host:port/database
    (因為我們裝了 mysqlclient 套件,Flask 和 SQLAlchemy 會自動使用 mysqlclient 來驅動)
    class Config():
        SQLALCHEMY_DATABASE_URI = "mysql://root:12345678@localhost/todo"
        SQLALCHEMY_TRACK_MODIFICATIONS = False # 關閉對資料庫修改的追蹤
    

引用

  • 在 __init__.py 中引用了 config.py 的 Config、extensions.py 的 db
    from .config import Config
    from .extensions import db
    app.config.from_object(Config)
    db.init_app(app)
    

分開設定的優點

  • 易管理:
    • 將各類套件的初始化、config 都放在同一個檔案中,更方便管理,要修改時只需要一個檔案即可
  • 復用性:
    • 未來在新的專案就可以重複利用,只需針對需求做出修改即可

建立 Member、Task 模型(db.model)

  • 在 python 中建立一個類別
    • 類別對應到資料庫中的 table(繼承 db.Model)
    • 類別的屬性對應到資料庫中的 column(使用 db.column)
    • 利用 db.column 的參數設定資料庫中 column 的屬性:
      • primary_key=True:主鍵。
      • unique=True:唯一值,不可重複。
      • nullable=False:不能為NULL。
      • String(255):字串長度 255。

Member

  • 利用我們已經建立好的資料庫來設定 Member 類別
    # MySQL
    CREATE TABLE member(
        id INT PRIMARY KEY AUTO_INCREMENT,
        username VARCHAR(255) UNIQUE NOT NULL,
        password_hash VARCHAR(500) NOT NULL,
        salt VARCHAR(255) NOT NULL,
        email VARCHAR(255) UNIQUE NOT NULL
    );
    
    # models.py
    from .extensions import db  # 引用 db 實例
    
    class Member(db.Model):
        id = db.Column(db.Integer, primary_key=True)
        username = db.Column(db.String(255), unique=True, nullable=False)
        password_hash = db.Column(db.String(500), nullable=False)
        salt = db.Column(db.String(255), nullable=False)
        email = db.Column(db.String(255), unique=True, nullable=False)
        tasks = db.relationship("Task", back_populates="member", cascade="all, delete-orphan")
    

Task

  • 同樣利用我們已經建立好的資料庫來設定 Task 類別
    # MySQL
    CREATE TABLE task(
        id INT PRIMARY KEY AUTO_INCREMENT,
        member_id INT NOT NULL,
        title VARCHAR(255) NOT NULL,
        priority ENUM('High', 'Medium', 'Low') DEFAULT "Medium",
        state ENUM('Todo', 'Doing', 'Done') DEFAULT "Todo",
        start DATETIME DEFAULT CURRENT_TIMESTAMP,
        deadline DATETIME DEFAULT (NOW() + INTERVAL 1 DAY),
        description TEXT,
        FOREIGN KEY (member_id) REFERENCES member(id) ON DELETE CASCADE
    );
    
    # models.py
    from .extensions import db  # 引用 db 實例
    
    class Task(db.Model):
        id = db.Column(db.Integer, primary_key=True)
        member_id = db.Column(db.Integer, db.ForeignKey("member.id"), nullable=False)
        title = db.Column(db.String(255), nullable=False)
        priority = db.Column(db.Enum('High', 'Medium', 'Low'), default="Medium")
        state = db.Column(db.Enum('Todo', 'Doing', 'Done'), default="Todo")
        start = db.Column(db.DateTime, server_default=func.current_timestamp())
        deadline = db.Column(db.DateTime, default=datetime.now() + timedelta(days=1))
        description = db.Column(db.Text, default="")
        member = db.relationship("Member", back_populates="tasks")
    

SQLAlchemy 功能 relationship

  • 我們在 Member、Task 分別給予了 tasks 與 member 的屬性使用 db.relationship
    tasks = db.relationship("Task", back_populates="member", cascade="all, delete-orphan")
    member = db.relationship("Member", back_populates="tasks")
    
  • 這個是為了在我們打 Member Tasks API 時能夠讓 SQLAlchemy 去使用我們設定的外鍵
    member_id = db.Column(db.Integer, db.ForeignKey("member.id"), nullable=False)
    
  • 最後只要使用 member.tasks 就可取得當前 member 所有的 task
    • 例如:
    member = Member.query.filter_by(id=id).first()
    all_tasks = member.tasks
    

嘗試使用 Flask 操控 MySQL

  • 建立一個簡單的 route 取得所有的 tasks 並轉成 json 回傳
@app.route('/tasks', methods=['GET'])
def get_all_tasks():
    # 查詢所有的 Task
    tasks = Task.query.all()

    # 組裝幾個 Task 的資料
    result = [{'id': task.id, 'title': task.title, 'priority': task.priority, 'state': task.state} for task in tasks]

    return jsonify(result)
  • 測試是否能取得資料
    • https://ithelp.ithome.com.tw/upload/images/20230923/20162291YJa8vqdPb5.png

Todo 更新 9/22

  1. 將錯誤提示改為在按鈕下方
  2. 登入框 RWD
  3. 後端改為 MVC 架構

上一篇
從零到全端:轉職者的 To-Do List 技能之旅-後端開發-環境建立
下一篇
從零到全端:轉職者的 To-Do List 技能之旅-後端開發- Flask-RESTX
系列文
從零到全端:轉職者的 To-Do List 技能之旅15
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言