从 pydantic 模型中的父静态方法访问子 class 属性

accessing child class attributes from parent static methods in pydantic models

给定一个花哨的 BaseModel class 定义如下:

from typing import List, Optional
from uuid import uuid4

from pydantic import BaseModel, Field
from server.database import get_db


class Campaign(BaseModel):
    id: Optional[str] = Field(default_factory=lambda: str(uuid4()))
    name: str

    @staticmethod
    async def all() -> List:
        ret = get_db()['campaigns'].find()
        return [Campaign(**i) async for i in ret]

    @staticmethod
    async def get(id):
        ret = await get_db()['campaigns'].find_one({'id': id})
        return Campaign(**ret)

    async def save(self):
        await get_db()['campaigns'].insert_one(self.dict())

    async def update(self, **kwargs):
        await get_db()['campaigns'].update_one(
            {'id': self.id},
            {'$set': kwargs},
        )

从上面的模型定义可以看出,函数[allgetsave,update]可以通用多个CRUD模型如下:

class Record(BaseModel):
    ...
    @staticmethod
    async def all() -> List:
        ret = get_db()['records'].find()
        return [Record(**i) async for i in ret]

    async def save(self):
        pass

class Fragment(BaseModel):
    ...
    @staticmethod
    async def all() -> List:
        ret = get_db()['fragments'].find()
        return [Fragment(**i) async for i in ret]

    async def save(self):
        pass

在不同 classes 的所有这些方法中,唯一的变化是 MongoDB 集合的名称。现在,我正在为不同的模型多次复制粘贴此代码。

是否有任何通用的解决方案,以便我可以将这些方法的定义封装在一个基础下 class 并希望遵循 DRY 原则(而不是现在公然破坏它)。

这样的代码配置可以是:

class BaseDBModel(BaseModel):
    async def save(self):
        await get_db()[self.__class__.Meta.collection_name].insert_one(self.dict())

    async def update(self, **kwargs):
        await get_db()[self.__class__.Meta.collection_name].update_one(
            {'id': self.id},
            {'$set': kwargs},
        )

class Campaign(BaseDBModel):
    id: Optional[str] = Field(default_factory=lambda: str(uuid4()))
    name: str

    class Meta:
        collection_name = 'campaigns'

但是,此代码仅适用于实例方法(即 saveupdate),不适用于 所有得到。我不确定父 class 静态方法将如何访问 python?

中子项的 Meta class

如对上述方法提出任何建议或改进,我们将不胜感激。

静态方法应该是 class 方法,以便 class 调用 方法作为第一个参数传递。例如,

class BaseDBModel(BaseModel):

    @classmethod
    async def all(cls) -> List:
        ret = get_db()[cls.Meta.collection_name].find()
        return [class(**i) async for i in ret]

    @classmethod
    async def get(id):
        ret = await get_db()[cls.Meta.collection_name]({'id': id})
        return class(**ret)

    async def save(self):
        await get_db()[self.Meta.collection_name].insert_one(self.dict())

    async def update(self, **kwargs):
        await get_db()[self.Meta.collection_name].update_one(
            {'id': self.id},
            {'$set': kwargs},
        )


class Campaign(BaseDBModel):
    id: Optional[str] = Field(default_factory=lambda: str(uuid4()))
    name: str
    
    class Meta:
        collection_name = 'campaigns'


class Record(Campaign):
    class Meta:
        collection_name = 'records'


class Fragment(Campaign):
    class Meta:
        collection_name = 'fragments'

(尚不清楚RecordFragment是否应该直接继承自CampaignBaseDBModel。)