<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
以論壇為例,有個介面返回貼文(posts)資訊,然後呢,來了新需求,說需要顯示貼文的 author 資訊。
此時會有兩種選擇:
在 posts 的 query 中 join 查詢 author 資訊,在返回 post 中新增諸如 author_id, author_name 之類的欄位。
{'post': 'v2ex', 'author_name': 'tangkikodo'}
根據 posts 的 ids , 單獨查詢 author 列表,然後把 author 物件迴圈新增到 post 物件中。
{'post':'v2ex', 'author': {'name': 'tangkikod'}}
方法 1 中,需要去修改 query, 還需要修改post的schema. 如果未來要加新欄位,例如使用者頭像的話,會需要修改兩處。
方法 2 需要手動做一次拼接。之後增減欄位都是在 author 物件的範圍內修改。
所以相對來說, 方法 2 在未來的可維護性會比較好。用巢狀物件的方式可以更好的擴充套件和維護。
方法2 的返回結構
[ { "id": 1, "post": "v2ex", "author": { "name": "tangkikodo", "id": 1 } }, { "id": 2, "post": "v3ex", "author": { "name": "tangkikodo2", "id": 1 } } ]
然而需求總是會變化,突然來了一個新的且奇怪的需求,要在 author 資訊中新增資料,顯示他最近瀏覽過的貼文。返回體變成了:
[ { "id": 1, "post": "v2ex", "author": { "name": "tangkikodo", "recent_views": [ { "id": 2, "post": "v3ex" }, { "id": 3, "post": "v4ex" } ] } } ]
那這個時候該怎麼弄呢?血壓是不是有點上來了。
根據之前的方法 2, 通常的操作是在獲取到authors資訊後, 關聯查詢author的recent_posts, 拼接回authors, 再將 authors 拼接回posts。 流程類似層層查詢再層層回拼。 虛擬碼類似:
# posts query posts = query_all_posts() # authors query authors_ids = fetch_unique_author_id(posts) authors = query_author(author_ids) recent_view_posts = fetch_recent_review_posts(author_ids) # 新需求 recent_view_maps = calc_view_mapping(recent_view_posts) # 新需求 # authors attach authors = [attach_posts(a, recent_view_maps) for a in authors] author_map = calc_author_mapping(authors) # posts attach posts = [attach_author(p, author_map) for p in posts]
莫名的會聯想到callback hell, 新增新的層級都會在程式碼中間部分切入。
反正想想就挺麻煩的對吧。要是哪天再巢狀一層呢? 程式碼改起來有點費勁, 如果你此時血壓有點高,那請繼續往下看。
那,有別的辦法麼? 這裡有個小輪子也許能幫忙。
祭出一個小輪子: allmonday/pydantic-resolve
以剛才的例子,要做的事情抽象成兩部分:
class AuthorLoader(DataLoader): async def batch_load_fn(self, author_ids): async with async_session() as session: # query authors res = await session.execute(select(Author).where(Author.id.in_(author_ids))) rows = res.scalars().all() # transform into pydantic object dct = defaultdict(dict) for row in rows: dct[row.author_id] = AuthorSchema.from_orm(row) # order by author_id return [dct.get(k, None) for k in author_ids] class RecentViewPostLoader(DataLoader): async def batch_load_fn(self, view_ids): async with async_session() as session: res = await session.execute(select(Post, PostVisit.visitor_id) # join 瀏覽中間表 .join(PostVist, PostVisit.post_id == Post.id) .where(PostVisit.user_id.in_(view_ids) .where(PostVisit.created_at < some_timestamp))) rows = res.scalars().all() dct = defaultdict(list) for row in rows: dct[row.visitor_id].append(PostSchema.from_orm(row)) # group 到 visitor return [dct.get(k, []) for k in view_ids]
class RecentPostSchema(BaseModel): id: int name: str class Config: orm_mode = True class AuthorSchema(BaseModel): id: int name: str img_url: str recent_views: Tuple[RecentPostSchema, ...] = tuple() def resolve_recent_views(self, loader=LoaderDepend(RecentViewPostLoader)): return loader.load(self.id) class Config: orm_mode = True class PostSchema(BaseModel): id: int author_id: int name: str author: Optional[AuthorSchema] = None def resolve_author(self, loader=LoaderDepend(AuthorLoader)): return loader.load(self.author_id) class Config: orm_mode = True
然後呢?
然後就沒有了,接下來只要做個 post 的查詢, 再簡單地...resolve 一下,任務就完成了。
posts = (await session.execute(select(Post))).scalars().all() posts = [PostSchema.from_orm(p) for p in posts] results = await Resolver().resolve(posts)
在拆分了 loader 和 schema 之後,對資料地任意操作都很簡單,新增任意新的schema 都不會破壞原有的程式碼。
完整的案例可以檢視 6_sqlalchemy_loaderdepend_global_filter.py
如果之前使用過aiodataloader 的話會知道,開發需要手動維護loader在每個request 中的初始化過程 , 但在 pydantic-resolve
中你完全不用操心非同步上下文的建立,不用維護DataLoader的範例化, 一切都在pydantic-resolve
的管理之中。
就完事了。如果必須說有啥缺點的話。。必須用 async await 可能算一個。
該專案已經在我司的生產環境中使用,並且保持了100%的測試覆蓋率。 歡迎大家嚐鮮體驗,如果遇到問題歡迎發issue,我會盡快修復。
以上就是pydantic resolve解決巢狀資料結構生成痛點分析的詳細內容,更多關於pydantic resolve巢狀資料結構的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45