[英]accessing child class attributes from parent static methods in pydantic models
给定一个 pydantic 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},
)
从上面的 model 定义可以看出,函数 [ all , get , save , 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
在不同类的所有这些方法中,唯一的变化是 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'
但是,此代码仅适用于实例方法(即save 、 update ),不适用于 static 方法,如all和get 。 我不确定父 class staticmethod 将如何访问 python 中子项的Meta class go?
对上述方法的任何建议或改进将不胜感激。
static 方法应该改为 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'
(尚不清楚Record
和Fragment
应该直接继承自Campaign
还是BaseDBModel
。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.