一种序列化 Django Model 的新思路 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
abersheeran
V2EX    Django

一种序列化 Django Model 的新思路

  •  
  •   abersheeran 2021 年 4 月 16 日 3429 次点击
    这是一个创建于 1818 天前的主题,其中的信息可能已经有所发展或是发生改变。
    from typing import Any, Dict, List from django.core.exceptions import FieldDoesNotExist from django.db import models def serialize_model(model: models.Model) -> Dict[str, Any]: result = { name: serialize_model(foreign_key) for name, foreign_key in model.__dict__["_state"].__dict__.get("fields_cache", {}).items() } for name, value in model.__dict__.items(): try: model._meta.get_field(name) except FieldDoesNotExist: continue else: result[name] = value for name, queryset in model.__dict__.get("_prefetched_objects_cache", {}).items(): result[name] = serialize_queryset(queryset) return result def serialize_queryset(queryset: models.QuerySet) -> List[Dict[str, Any]]: return [serialize_model(model) for model in queryset] 

    发 V2EX 上给各位大佬看就不写那么多前后文的废话了。直接根据 Django Model 存储设计进行序列化,不需要定义额外的模型,不需要担心 N+1 查询。在我博客《一种序列化 Django model 的新思路》可以看看前因后果。

    16 条回复    2021-04-17 11:55:16 +08:00
    maocat
        1
    maocat  
       2021 年 4 月 16 日
    很好的东西,再来个装饰器封装一下直接返回结果了

    可惜的是很多人不遵从,他们热爱用 property 添加各式各样的属性,一点都不关心这种是不是需要优化
    ericls
        2
    ericls  
       2021 年 4 月 16 日 via iPhone   1
    要解决 N+1 问题 不能从单个 model 入手……
    一定要从一个 request 的全局入手
    ericls
        3
    ericls  
       2021 年 4 月 16 日 via iPhone   1
    先记录一个 request 中一共需要哪些东西 才有可能知道怎么去优化查询

    另外你这个治标不治本 只是把原来要查询的地方变成 None. Restful 的查询本来就是固定的 所以这种地方只在 dev 环境中和 CI 里面报错 上线环境就不会出错。
    另外这个问题也是 restful 自己的问题 如果一个请求拿不到我要的数据 我自然会发一个新的请求……
    abersheeran
        4
    abersheeran  
    OP
       2021 年 4 月 16 日
    @ericls 我说的 N+1 是 Django ORM 导致的 N+1 。业务上的 N+1 问题是另一回事。
    ericls
        5
    ericls  
       2021 年 4 月 16 日 via iPhone   1
    @abersheeran 你只是把 n+1 变成了 没有兑现的承诺而已 restful 返回格式很固定 与其破坏承诺 不如在 dev 和自己 test 让 n+1 报错 生产环境就不会 n*1 了…… 类似 type checking 的思路
    23333333333
        6
    23333333333  
       2021 年 4 月 16 日
    我感觉用了一些字符串和一些内部接口 比如._state

    这些就稍微有点不妥?
    ericls
        7
    ericls  
       2021 年 4 月 16 日 via iPhone
    @23333333333 Python library 就得这么写才爽
    nine
        8
    nine  
       2021 年 4 月 16 日   1
    Rails 欢迎你
    abersheeran
        9
    abersheeran  
    OP
       2021 年 4 月 16 日
    @ericls 目前来说,这已经是最佳的解决方法了不显式的自己写预查询,这个序列化功能就不会给序列化外键数据。至于你说的 check,你可以试试给 Django 提 PR,反正我不抱有任何乐观看法。
    abersheeran
        10
    abersheeran  
    OP
       2021 年 4 月 16 日
    @23333333333 还行,Django 官方不提供接口,只能自己找方法了。说实话,这玩意让 Django 自己来做更好,奈何那群人不知道天天在想什么。你看这么多年都不支持 PUT 的请求体解析、TestClient 不支持 PUT 提交多段表单格式的数据。反正我现在不用 Django 。如果不是朋友找我帮忙,这个序列化方法我可能会让它一直停留在脑海里。
    abersheeran
        11
    abersheeran  
    OP
       2021 年 4 月 16 日
    @maocat 这个是小问题。合并两个字典列表,一行代码就够了。
    ericls
        12
    ericls  
       2021 年 4 月 16 日 via iPhone
    @abersheeran 你都用了这么多私有方法了 离 monkey patch 还远吗 况且只需要开发个测试环境中
    ericls
        13
    ericls  
       2021 年 4 月 16 日 via iPhone
    @abersheeran Django 也是标准 wsgi/asgi 任何不支持的东西裸写 wsgi/asgi 即可…… 甚至可以和别的框架混用 我经常这么干
    abersheeran
        14
    abersheeran  
    OP
       2021 年 4 月 16 日
    @ericls 你先自己试试再说吧。Talk is cheap
    ericls
        15
    ericls  
       2021 年 4 月 17 日
    @abersheeran 试了一下 没有想象中 hacky

    继承一下 ForwardManyToOneDescriptor 把 get_object 改成直接报错,然后继承 ForeignKey 把 class attribute `forward_related_accessor_class` 改成刚刚创建的 class 就搞定了。`related_accessor_class` 同理。当然,这个需要把用到原生 ForeignKey 的地方都替换了,所以比较推荐 monkey patch `django.db.models.fields.related_descriptors` 里面的方法 这个文件前面有详细的说明.
    abersheeran
        16
    abersheeran  
    OP
       2021 年 4 月 17 日 via Android
    @ericls 行。等你搞完,我去给你 star
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5664 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 36ms UTC 06:43 PVG 14:43 LAX 23:43
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86