簡體   English   中英

使用 Pydantic 子 model 管理父 model 的默認值集

[英]Use Pydantic child model to manage sets of default values for the parent model

我正在使用 pydantic 來管理支持不同數據集的應用程序的設置。 每個都有一組可覆蓋的默認值,但每個數據集它們是不同的。 目前,我通過驗證器正確實現了所有邏輯:

from pydantic import BaseModel

class DatasetSettings(BaseModel):
    dataset_name: str 
    table_name: str

    @validator("table_name", always=True)
    def validate_table_name(cls, v, values):
        if isinstance(v, str):
            return v
        if values["dataset_name"] == "DATASET_1":
            return "special_dataset_1_default_table"
        if values["dataset_name"] == "DATASET_2":
            return "special_dataset_2_default_table"
        return "default_table"

class AppSettings(BaseModel):
    dataset_settings: DatasetSettings
    app_url: str

這樣,我會根據dataset_name獲得不同的默認值,但用戶可以在必要時覆蓋它們。 這是期望的行為。 問題是,一旦有多個這樣的字段和名稱,閱讀和維護就會變得一團糟。 似乎繼承/多態可以解決這個問題,但 pydantic 工廠邏輯似乎過於硬編碼,無法實現,尤其是嵌套模型。

class Dataset1Settings(DatasetSettings):
    dataset_name: str = "DATASET_1"
    table_name: str = "special_dataset_1_default_table"

class Dataset2Settings(DatasetSettings):
    dataset_name: str = "DATASET_2"
    table_name: str = "special_dataset_2_default_table"

def dataset_settings_factory(dataset_name, table_name=None):
    if dataset_name == "DATASET_1":
        return Dataset1Settings(dataset_name, table_name)
    if dataset_name == "DATASET_2":
        return Dataset2Settings(dataset_name, table_name)
    return DatasetSettings(dataset_name, table_name)

class AppSettings(BaseModel):
    dataset_settings: DatasetSettings
    app_url: str

我考慮過的選項:

  • 創建一組新的默認數據集設置模型,覆蓋DatasetSettings__init__ ,實例化子類並將其屬性復制到父 class 中。 有點笨重。
  • 使用dataset_settings_factory覆蓋AppSettings__init__以設置AppSettingsdataset_settings屬性。 不太好,因為默認行為在DatasetSettings中根本不起作用,只有在AppSettings中實例化為嵌套的 model 時才起作用。

我希望Field(default_factory=dataset_settings_factory)可以工作,但default_factory僅適用於實際默認值,因此它的參數為零。 是否有其他方法可以攔截特定 pydantic 字段的參數並使用自定義工廠?

另一種選擇是使用Discriminate/Tagged Unions

但是您的解決方案(沒有詳細查看)看起來也不錯。

我最終解決了第一個選項后的問題,如下所示。 代碼可使用 pydantic 1.8.2 和 pydantic 1.9.1 運行。

from typing import Optional
from pydantic import BaseModel, Field


class DatasetSettings(BaseModel):
    dataset_name: Optional[str] = Field(default="DATASET_1")
    table_name: Optional[str] = None

    def __init__(self, **data):
        factory_dict = {"DATASET_1": Dataset1Settings, "DATASET_2": Dataset2Settings}
        dataset_name = (
            data["dataset_name"]
            if "dataset_name" in data
            else self.__fields__["dataset_name"].default
        )
        if dataset_name in factory_dict:
            data = factory_dict[dataset_name](**data).dict()
        super().__init__(**data)


class Dataset1Settings(BaseModel):
    dataset_name: str = "DATASET_1"
    table_name: str = "special_dataset_1_default_table"


class Dataset2Settings(BaseModel):
    dataset_name: str = "DATASET_2"
    table_name: str = "special_dataset_2_default_table"


class AppSettings(BaseModel):
    dataset_settings: DatasetSettings = Field(default_factory=DatasetSettings)
    app_url: Optional[str]


app_settings = AppSettings(dataset_settings={"dataset_name": "DATASET_1"})
assert app_settings.dataset_settings.table_name == "special_dataset_1_default_table"
app_settings = AppSettings(dataset_settings={"dataset_name": "DATASET_2"})
assert app_settings.dataset_settings.table_name == "special_dataset_2_default_table"

# bonus: no args mode
app_settings = AppSettings()
assert app_settings.dataset_settings.table_name == "special_dataset_1_default_table"

在此過程中我發現了幾個問題:

  1. 如果Dataset1Settings繼承自DatasetSettings ,它將進入一個遞歸循環,在 init 和 infinitum 上調用 init。 這可以通過一些內省來打破,但我選擇了鴨式方法。
  2. 當前的解決方案會破壞DatasetSettings上的所有驗證器。 我確信無論如何都有一種調用驗證邏輯的方法,但是當前的解決方案通過僅使用super().__init__進行初始化,有效地回避了您擁有的任何類級驗證
  3. 同樣的事情也適用於BaseSettings對象,但你必須拖動它們繁瑣的 init args:
    def __init__(
        self,
        _env_file: Union[Path, str, None] = None,
        _env_file_encoding: Optional[str] = None,
        _secrets_dir: Union[Path, str, None] = None,
        **values: Any
    ):
        ...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM