本文环境
- Python 3.6.5
- Django 2.0.4
fix(2018.5.19):最近得知Django 的model基类需要声明为abstract,故在原来的代码加入abstract声明,以免误导
在Django中,关于如何将model类序列化为json,一般的话有两a器
将model类转为字典,再使用json库的dumps方法转为json
第一种方法就不多讲了,直接去看官方文档就好啦
一般来说,官方提供的方法应该都是比较好用和稳定的,然而,使用官方的序列化器却问题不少:
格式丑陋,格式如下,一言难尽:
1
2
3
4
5
6
7
8
9
10
|
[ { "pk" : "4b678b301dfd8a4e0dad910de3ae245b" , "model" : "sessions.session" , "fields" : { "expire_date" : "2013-01-16T08:16:59.844Z" , ... } } ] |
是的,其中pk指的是默认主键,model指的是该object的model类型,然后fields才是obj的各种字段...真的是不知如何评价了
- 不能很好地支持list
- 对于一些外键(包括ManyToManyField等)不是很友好
- 甚至对于自身的DateField也没有很好的支持
数了一通官方序列化器的缺点,当然了,上面的几个点肯定是有解决方案的,但是啊,我确实不想折腾了嘤嘤嘤。
于是扔出我的解决方案:
- 新建一个类BaseModel,此类继承于官方的model类django.db.models.Model
- 在着个BaseModel中,声明一个方法,此方法用于生成关于这个object的字典
- 使用这个object的字典生成json
关于生成object的字典的策略是这样的:
- 通过反射获取这个object的所有字段名
- 根据字段名获得某个字段field
- 如果filed的类型的是int、float、str的话,直接将以 "字段名":字段值 的形式放入字典中
- 若field的类型是datetime或者date的话,使用date的方式处理,然后放入字典
- 若field的类型是BaseModel的话,那么就调用该field的getDict方法递归获得该field对应的字典,然后放入字典中
- 若field的类型是ManyToMany类型,在具体草种中我们使用这个field的all方法来这个field的所有object,然后也是通过getDict方法将其放入到字典中
源码及使用方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
from django.db import models import json class BaseModel(models.Model): class Meta: abstract = True # 返回self._meta.fields中没有的,但是又是需要的字段名的列表 # 形如['name','type'] def getMtMField( self ): pass # 返回需要在json中忽略的字段名的列表 # 形如['password'] def getIgnoreList( self ): pass def isAttrInstance( self , attr, clazz): return isinstance ( getattr ( self , attr), clazz) def getDict( self ): fields = [] for field in self ._meta.fields: fields.append(field.name) d = {} import datetime for attr in fields: if isinstance ( getattr ( self , attr), datetime.datetime): d[attr] = getattr ( self , attr).strftime( '%Y-%m-%d %H:%M:%S' ) elif isinstance ( getattr ( self , attr), datetime.date): d[attr] = getattr ( self , attr).strftime( '%Y-%m-%d' ) # 特殊处理datetime的数据 elif isinstance ( getattr ( self , attr), BaseModel): d[attr] = getattr ( self , attr).getDict() # 递归生成BaseModel类的dict elif self .isAttrInstance(attr, int ) or self .isAttrInstance(attr, float ) \ or self .isAttrInstance(attr, str ): d[attr] = getattr ( self , attr) # else: # d[attr] = getattr(self, attr) mAttr = self .getMtMField() if mAttr is not None : for m in mAttr: if hasattr ( self , m): attlist = getattr ( self , m). all () l = [] for attr in attlist: if isinstance (attr, BaseModel): l.append(attr.getDict()) else : dic = attr.__dict__ if '_state' in dic: dic.pop( '_state' ) l.append(dic) d[m] = l # 由于ManyToMany类不能存在于_meat.fields,因而子类需要在getMtMFiled中返回这些字段 if 'basemodel_ptr' in d: d.pop( 'basemodel_ptr' ) ignoreList = self .getIgnoreList() if ignoreList is not None : for m in ignoreList: if d.get(m) is not None : d.pop(m) # 移除不需要的字段 return d def toJSON( self ): import json return json.dumps( self .getDict(), ensure_ascii = False ).encode( 'utf-8' ).decode() |
使用方法:
models的所有类都继承BaseModel类,然后调用此类的toJSON()方法即可
注意,不知为何,self._meta.fields中没有包含ManyToManyField字段,因而需要重写getMtMField方法。例子如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class Book(BaseModel): name = models.CharField(max_length = 50 ) authors = models.ManyToManyField(Author) publish = models.ForeignKey(Publisher, on_delete = models.SET_NULL, blank = True , null = True ) page = models.IntegerField(default = 0 ) # 页数 introduction = models.CharField(max_length = 500 ) bookType = models.ManyToManyField(BookType, null = True , blank = True ) bookTag = models.ManyToManyField(BookTag, null = True , blank = True ) evaluation = models.FloatField() coverUrl = models.CharField(max_length = 100 , null = True , blank = True ) def getMtMField( self ): return [ 'bookType' , 'bookTag' ] |
结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
{ "id" :4, "name" : "Django从入门到放弃" , "page" :123, "introduction" : "introduction" , "evaluation" :1, "bookType" :[ { "id" :1, "name" : "类型" } ], "bookTag" :[ { "id" :2, "name" : "tag" } ] } |
后记
源码有引用,即getDict方法中的第一个for循环,但懒得找原链接了,望见谅,特此声明;
- 本人python新手,代码多有不规范之处,望见谅;
- 代码不精,但是也希望能帮到你_;
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:https://www.jianshu.com/p/c3db3cc2c80a