iT邦幫忙

2023 iThome 鐵人賽

DAY 6
0
Software Development

玩轉 Python 與 MongoDB系列 第 6

玩轉 Python 與 MongoDB_Day06_Pydantic 基本介紹

  • 分享至 

  • xImage
  •  

在前兩個章節當中,我們可以看到 MongoDB 對於資料並沒有特別去做什麼規範,基本上是給什麼寫什麼,可想而知如果沒有一個既定的格式的話,資料操作起來會相當不容易,今天我們會介紹 Pydantic 這個套件,他主要是用來建立資料模型,並且提供資料驗證的方式

今天我們主要介紹的內容如下:

  • Pydantic 模型建立
  • Pydantic 資料驗證
  • Pydantic 結合 Enum
  • Pydantic 轉換成 dictionary

明天我們會教學如何將 Pydantic 實際與 MongoDB 的資料進行結合

一、Pydantic 模型建立

要透過 Pydantic 建立資模型,我們只需要 import 該套件下的 BaseModel 類別即可,下方為一個簡易的範例

可以看到範例當中,使用 type hint 的方式進行資料的型態驗證,並且可以透過設定 Optional 來指定某個欄位是否需要,若沒有輸入則預設為None,同時我們也可以給予某個欄位預設值,當沒有輸入該欄位時,Pydantic 會自動代入預設值給該欄位

from typing import Optional
from datetime import datetime
from pydantic import BaseModel


class UserModel(BaseModel):
    name: str
    birthday: datetime
    email: Optional[str]
    created_time: datetime = datetime.now()

下方為實際利用此模型實體化幾個物件,總共有以下三種情況:

  • 正常資料寫入
  • 缺少 Optional 欄位
  • 缺少非 Optional 欄位
from typing import Optional
from datetime import datetime
from pydantic import BaseModel


class UserModel(BaseModel):
    name: str
    birthday: datetime
    email: Optional[str]
    created_time: datetime = datetime.now()


# 正常寫入資料
user = UserModel(
    name="nick",
    birthday=datetime.now(),
    email="nickchen1998@gmail.com"
)

print("------正常寫入資料------")
print(user)

# 缺少 Optional
user = UserModel(
    name="nick",
    birthday=datetime.now()
)
print("------缺少 Optional------")
print(user)

print("------缺少非 Optional------")
try:
    user = UserModel(
        birthday=datetime.now(),
        email="nickchen1998@gmail.com"
    )
except Exception as ex:
    print(ex.__class__.__name__)

下方圖片中可以看到三種結果:

  • 預設值欄位會自動代入預設值
  • 若缺少的欄位為 Optional 欄位,該欄位會被設為 None
  • 若缺少的欄位為非 Optional 欄位,則會成功將錯誤訊息印出,且錯誤訊息為驗證錯誤 (ValidationError)

model 基本應用

二、Pydantic 資料驗證

我們可以透過 import Pydantic 套件底下的 validator 來進行資料驗證,下方程式碼範例當中,我們透過裝飾器的方式來標註我們要驗證的欄位名稱並建立一個 function 來進行驗證

注意:手動引發的錯誤類別必須為 ValueError,放入其他種類的錯誤似乎會失效

from typing import Optional
from datetime import datetime
from pydantic import BaseModel, validator, ValidationError


class UserModel(BaseModel):
    name: str
    birthday: datetime
    email: Optional[str]
    created_time: datetime = datetime.now()

    @validator("birthday")
    def over_18(cls, birthday: datetime):
        if datetime.now().year - birthday.year < 18:
            raise ValueError("年紀必須大於 18 歲")
        return birthday


print("----大於 18 歲----")
user = UserModel(
    name="nick", birthday=datetime(year=1999, month=1, day=1)
)
print(user)

print("----小於 18 歲----")
try:
    user = UserModel(
        name="nick", birthday=datetime(year=2023, month=1, day=1)
    )
    print(user)
except ValidationError as ex:
    print(ex)

可以看到下圖當中第一次有成功輸出完整的 user 模型,第二次則成功的印出驗證錯誤的錯誤訊息

model 資料驗證

三、Pydantic 與 Enum 結合

Enum 時常會做為列舉常數使用,比如 Log 的錯誤等級、任務狀態等等,下方為常見的 Log 錯誤等級的 Enum 範例

from enum import Enum


class LogLevel(Enum):
    DEBUG = "debug"
    INFO = "info"
    WARNING = "warning"
    ERROR = "error"
    CRITICAL = "critical"

下方的範例中我們可以看到我們在 model 裡面使用了 LogLevel 這個 Enum 並且增加了 Config 這個類別,其中也把 use_enum_values 這欄位設為 True,這將會在我們將模型轉換為 dictionary 的時候自動將 Enum 型別的欄位轉為字串

更多 Config 設定可以參考 這個網址

注意:由於 use_enum_values 會自動協助將 Enum 轉換為 string,因此判斷相等時需要透過取得 Enum 底下的 value 屬性來做判斷

from enum import Enum
from pydantic import BaseModel


class LogLevel(Enum):
    DEBUG = "debug"
    INFO = "info"
    WARNING = "warning"
    ERROR = "error"
    CRITICAL = "critical"


class LogModel(BaseModel):
    level: LogLevel
    message: str

    class Config:
        use_enum_values = True


log_list = [
    LogModel(level=LogLevel.DEBUG, message="This is debug level log."),
    LogModel(level=LogLevel.INFO, message="This is info level log.")
]

for log in log_list:
    if log.level == LogLevel.INFO.value:
        print(log.message)

下圖中我們可以看到比對到 info 等級的 log 的時候成功將其 message 印出

model enum

四、Pydantic 轉換 Dictionary

我們可以透過呼叫 BaseModel 下的 dict() 方法來將 model 轉換為 dictionary,轉換後我們就可以將該資料直接寫進 MongoDB 當中,下方為簡易的範例

from enum import Enum
from pydantic import BaseModel


class LogLevel(Enum):
    DEBUG = "debug"
    INFO = "info"
    WARNING = "warning"
    ERROR = "error"
    CRITICAL = "critical"


class LogModel(BaseModel):
    level: LogLevel
    message: str

    class Config:
        use_enum_values = True


log = LogModel(level=LogLevel.WARNING, message="This is warning log.")
print(log.dict())

下圖中可以看到我們印出的資料已經成功轉換為 dictionary,且由於有設定 use_enum_values = True,和 enum 有關的欄位也自動被轉換為字串

model dict


上一篇
玩轉 Python 與 MongoDB_Day05_GridFS 大型資料 & 檔案插入
下一篇
玩轉 Python 與 MongoDB_Day07_Pydantic 與 MongoDB
系列文
玩轉 Python 與 MongoDB30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言