Django model 转字典的几种方法 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
37Y37
V2EX    Python

Django model 转字典的几种方法

  •  
  •   37Y37 2018-10-13 09:29:46 +08:00 4164 次点击
    这是一个创建于 2623 天前的主题,其中的信息可能已经有所发展或是发生改变。

    平常的开发过程中不免遇到需要把 model 转成字典的需求,尤其是现在流行前后端分离架构,Json 格式几乎成了前后端之间数据交换的标准,这种 model 转 dict 的需求就更多了,本文介绍几种日常使用的方法以供参考,所有例子均基于 Django 2.0 环境演示

    背景介绍

    model 内容如下:

    class Group(models.Model): name = models.CharField(max_length=255, unique=True, verbose_name='组名称') def __str__(self): return self.name class User(models.Model): create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间') username = models.EmailField(max_length=255, unique=True, verbose_name='用户名') fullname = models.CharField(max_length=64, null=True, verbose_name='中文名') is_active = models.BooleanField(default=True, verbose_name='激活状态') leader = models.ForeignKey('self', null=True, on_delete=models.CASCADE, verbose_name='上级') group = models.ManyToManyField(Group, null=True, verbose_name='所属组') def __str__(self): return self.username 

    需求很简单就是分别把 Group 和 User 表中的数据转换成字典格式返回

    方法一:直接构建字典

    示例代码:

    >>> _t = Group.objects.get(id=1) >>> >>> dict = { ... 'id': _t.id, ... 'name': _t.name ... } >>> >>> print(dict) {'name': 'GroupA', 'id': 1} 

    这种方法的好处是方便控制最终返回字典 value 的格式,例如对于 User 表,我想返回最终的数据是 id、创建时间、中文名、上级中文名、所属组名列表的话可以用下边的代码实现

    >>> _t = User.objects.get(id=2) >>> >>> dict = { ... 'id': _t.id, ... 'create_time': _t.create_time.strftime('%Y-%m-%d %H:%M:%S'), ... 'fullname': _t.fullname if _t.fullname else None, ... 'leader': _t.leader.fullname if _t.leader else None, ... 'group': [ i.name for i in _t.group.all() ], ... } >>> >>> print(dict) {'fullname': '运维咖啡吧', 'group': ['GroupA', 'GroupC', 'GroupE'], 'create_time': '2018-10-12 21:20:19', 'id': 2, 'leader': '公众号'} >>> 

    缺点也很明显,就是如果一个 model 字段很多且不需要转换 value 格式的时候需要写大量冗余的代码,这种问题怎么解决呢?且看下边的方法介绍

    方法:__dict__

    示例代码:

    >>> Group.objects.get(id=1).__dict__ {'id': 1, 'name': 'GroupA', '_state': <django.db.models.base.ModelState object at 0x7f68612daef0>} >>> >>> User.objects.get(id=1).__dict__ {'is_active': True, '_state': <django.db.models.base.ModelState object at 0x7f68612fa0b8>, 'id': 1, 'username': '[email protected]', 'leader_id': None, 'fullname': '公众号', 'update_time': datetime.datetime(2018, 10, 12, 17, 49, 35, 504141), 'create_time': datetime.datetime(2018, 10, 12, 16, 9, 7, 813660)} 

    这种方法优点就是写法简单,容易理解,代码量还少

    但会发现多了个没用的_state字段,同时 Foreignkey 字段名多了_id,也没有 ManyToManyField 字段的数据,且不能按需显示输出,当我只需要其中几个字段时会有大量冗余数据

    方法三:model_to_dict

    示例代码:

    >>> model_to_dict(Group.objects.get(id=1)) {'name': 'GroupA', 'id': 1} >>> >>> model_to_dict(User.objects.get(id=2)) {'leader': 1, 'is_active': True, 'username': '[email protected]', 'fullname': '运维咖啡吧', 'group': [<Group: GroupA>, <Group: GroupC>, <Group: GroupE>], 'id': 2} 

    这种方法能满足大部分的需求,且输出也较为合理,同时还有两个参数fieldsexclude来配置输出的字段,例如:

    >>> model_to_dict(User.objects.get(id=2), fields=['fullname','is_active']) {'is_active': True, 'fullname': '运维咖啡吧'} >>> >>> model_to_dict(User.objects.get(id=2), exclude=['group','leader','id']) {'fullname': '运维咖啡吧', 'is_active': True, 'username': '[email protected]'} 

    但是会跳过有editable=False属性字段的展示,对于有auto_now_add=Trueauto_now=True属性的 datetime 字段会默认添加editable=False隐藏属性,这也是上边两个 time 相关字段create_timeupdate_time转换成 dict 后不显示的原因,官方相关源码如下:

    for f in chain(opts.concrete_fields, opts.private_fields, opts.many_to_many): if not getattr(f, 'editable', False): continue 

    方法四:自定义 to_dict

    示例代码:

    from django.db.models.fields import DateTimeField from django.db.models.fields.related import ManyToManyField class User(models.Model): ... def to_dict(self, fields=None, exclude=None): data = {} for f in self._meta.concrete_fields + self._meta.many_to_many: value = f.value_from_object(self) if fields and f.name not in fields: continue if exclude and f.name in exclude: continue if isinstance(f, ManyToManyField): value = [ i.id for i in value ] if self.pk else None if isinstance(f, DateTimeField): value = value.strftime('%Y-%m-%d %H:%M:%S') if value else None data[f.name] = value return data 

    执行结果:

    >>> User.objects.get(id=2).to_dict() {'is_active': True, 'update_time': '2018-10-12 21:21:39', 'username': '[email protected]', 'id': 2, 'leader': 1, 'group': [1, 3, 5], 'create_time': '2018-10-12 21:20:19', 'fullname': '运维咖啡吧'} >>> >>> User.objects.get(id=2).to_dict(fields=['fullname','is_active','create_time']) {'is_active': True, 'fullname': '运维咖啡吧', 'create_time': '2018-10-12 21:20:19'} >>> >>> User.objects.get(id=2).to_dict(exclude=['group','leader','id','create_time']) {'is_active': True, 'update_time': '2018-10-12 21:21:39', 'username': '[email protected]', 'fullname': '运维咖啡吧'} 

    拥有model_to_dict一样的便利性,同时也解决了不能输出 time 时间字段( editable=False )的问题,还能对 value 按照自己需要的格式输出,一举多得 当然拥有便利性的同时需要自己实现to_dict的代码,增加了复杂度


    长按关注公众号查看更多原创文章

    如果你觉得文章对你有帮助,请转发分享给更多的人。如果你觉得读的不尽兴,推荐阅读以下文章:

    12 条回复    2018-10-15 18:21:08 +08:00
    Zzdex
        1
    Zzdex  
       2018-10-13 10:12:43 +08:00 via iPhone   1
    为啥不用 drf 呢
    WilliamYang
        2
    WilliamYang  
       2018-10-13 13:34:54 +08:00
    哎, 这水平
    37Y37
        3
    37Y37  
    OP
       2018-10-13 18:22:59 +08:00
    @Zzdex 有用 drf,如果项目就是前后端分离的 drf 确实是个不错的选择,某些项目比较轻没用到 drf 可以考虑这些方法
    37Y37
        4
    37Y37  
    OP
       2018-10-13 18:23:49 +08:00
    @WilliamYang 哪里有问题欢迎指出,非专业程序员,多跟大佬学习
    wellCh4n
        5
    wellCh4n  
       2018-10-13 21:48:29 +08:00
    我不知道这水平咋了,我觉得分析的不错啊…
    ffffish
        6
    ffffish  
       2018-10-13 22:22:54 +08:00
    这不是序列化问题吗,另外可以考虑 protobuf 之类的东西
    JasperYanky
        7
    JasperYanky  
       2018-10-13 23:13:20 +08:00
    不能直接用 drf 的 serializers 么? 定义一个 ModelSerializer 直接就转成 dict 了额~
    37Y37
        8
    37Y37  
    OP
       2018-10-13 23:33:52 +08:00
    @wellCh4n 感谢
    @ffffish 就是序列化的问题,我去看下 protobuf
    @JasperYanky 如果有用 drf,那 serializers 就简单多了,我上边回答里提了有些项目很小没有用 drf
    8e47e42
        9
    8e47e42  
       2018-10-14 11:31:06 +08:00 via iPhone
    我以为是提问,结果发现居然是教程
    sglyqfh
        10
    sglyqfh  
       2018-10-14 16:06:12 +08:00
    挺好的
    silhouette
        11
    silhouette  
       2018-10-14 18:35:35 +08:00 via Android
    drf 多好,serializer 一把梭
    metamask
        12
    metamask  
       2018-10-15 18:21:08 +08:00
    1.
    https://docs.djangoproject.com/en/2.0/topics/serialization/

    queryset
    from django.core import serializers
    data = serializers.serialize("json", SomeModel.objects.all())

    2.
    drf
    from . import serializers
    serializers.Serializer(obj).data

    小项目没用 drf,直接装一个,写个 model serializer 然后用上,比手动处理快捷很多
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3331 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 39ms UTC 04:43 PVG 12:43 LAX 20:43 JFK 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