Sqlalchemy 資料模型序列化(轉JSON)

songofhawk發表於2022-11-23

Sqlalchemy 可以很方便地做ORMapping,把資料庫記錄對映為業務實體類的例項,例如下面這樣:

class Student(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100))
    city = db.Column(db.String(50))
    addr = db.Column(db.String(200))
    pin = db.Column(db.String(10))


if __name__ == '__main__':
    student = Student.query.get(1)
    

但這種例項不方便序列化,比如要想在 Flask Web Service 介面中返回這個物件:

@app.route('/demo/db', methods=['GET'])
def demo_db():
    student = Student.query.get(1)
    return jsonify(student)

或者是直接轉 JSON 字串:

import json

json.dumps(student)

都會報錯:Object of type Student is not JSON serializable

在網上搜的話,會有五花八門的答案,大部分都是讓你實現某個類似 to_json 的方法,有些根本不管用,有些很麻煩。
其實最簡單的解決方案就是:

用 python 3.7 引入的 dataclass 裝飾器,這個裝飾器幫我們實現了對應的方法,可以直接把類變數宣告當做例項變數的定義:

from dataclasses import dataclass


@dataclass
class Student(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100))
    city = db.Column(db.String(50))
    addr = db.Column(db.String(200))
    pin = db.Column(db.String(10))

這時候,再呼叫 jsonify(student)就不會報錯了,但奇怪的是,結果是個空物件:{}
原來 dataclass 裝飾器“認可”的欄位宣告,必須有 type annotation,改成下面這樣:

from dataclasses import dataclass


@dataclass
class Student(db.Model):
    id: int = db.Column(db.Integer, primary_key=True)
    name: str = db.Column(db.String(100))
    city: str = db.Column(db.String(50))
    addr: str = db.Column(db.String(200))
    pin: str = db.Column(db.String(10))

輸出結果就完美了:

{
  addr: "xxx",
  city: "bj",
  id: 1,
  name: "hui",
  pin: "xyz"
}

不過jsonify雖然可用了,json.dumps依然會報錯,最簡單的辦法,是先把物件轉為 dict,再轉 json:

import json
from dataclasses import dataclass

print(json.dumps(dataclasses.asdict(student)))

相關文章