django-restful-framework的序列化器嵌套递归调用

django接口开发,使用序列化器的嵌套调用返回Json格式数据


一、背景:

目前正在使用django的restful风格的接口开发,需求是实现数据流加载,后端写好接口,前端调用接口,解析json数据,动态的添加dom元素。我想要将不同的model整合在一起,进行序列化,那么如何整合这些不同的model呢?


方法一:

根据官方文档找到的答案:

采用多个序列化进行嵌套:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

class ProfileSerializer(serializer,ModelSerializer):
class Meta:
model = profle
fields = '__all__'

class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer() # 这是为另一个序列化器

class Meta:
model = User
fields = ['username', 'email', 'profile']

def create(self, validated_data):
profile_data = validated_data.pop('profile') # 返回得是一个querydict,它继承了dict字典
user = User.objects.create(**validated_data)
Profile.objects.create(user=user, **profile_data)
return user


法二:

常用的是以下这种嵌套,因为这种方法不仅可以嵌套,还可以递归调用序列化器,只需要在get_commodity中返回的是当前这个序列化器,就可以了,可以用于评论的多级递归序列化调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Order_detailsSerializer(serializers.ModelSerializer):
"""
订单商品详情序列化容器
The serializer of Order_details which used to combine with Order_basic
"""

commodity = serializers.SerializerMethodField() # 商品细节

def get_commodity(self, obj):
commodity = Commodity.commodity_.filter(order_details=obj.pk)
if len(commodity) > 0:
return CommoditySerializer(commodity, many=True).data
return ''

class Meta:
model = Order_details
fields = ('price', 'commodity', 'commodity_counts')

使用serializers.SerializerMethodField()指明所要嵌套的字段。然后重写get_commodity的方法,获取另一个序列化器的数据,作为该字段的值。

这样一来,我就可以序列化多个model,组成json格式的数据

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
[
{
"order_id": 1,
"trade_number": "102301230213021",
"total_price": "9999.0",
"commodity_total_counts": 100,
"generate_time": "2020-05-26T15:07:08",
"status": "1",
"order_details": [
{
"price": "50.00",
"commodity": [
{
"store_name": "店铺1",
"commodity_name": "貂皮大衣",
"intro": "舒适的不要不要的",
"category": "衣服",
"discounts": false,
"freight": 0,
"image": null
}
],
"commodity_counts": 25
},
{
"price": "100.00",
"commodity": [
{
"store_name": "店铺1",
"commodity_name": "貂皮大衣",
"intro": "舒适的不要不要的",
"category": "衣服",
"discounts": false,
"freight": 0,
"image": null
}
],
"commodity_counts": 25
}
]
},

二、自定义序列化格式

但问题又来了,我之前设计数据表的时候,针对choices字段的二元祖,我为了减少数据库的字段所使用的数据类型大小,采用1个字节的字符串来代替具体的汉子,比如

1
2
3
4
5
6
7
8
9
10
# 订单状态
status_choice = (
("1", '代付款'), # 用户提交订单,尚未付款,此时会锁定库存
("2", '代发货'), # 用户付款后,等待商家接单前
("3", '交易成功'), # 用户确认收货之后,订单完成交易
("4", '已取消'), # 付款前取消订单
("5", '售后中'), # 商家发货或付款后,用户取消订单
("6", '交易关闭'), # 取消订单或售后结束都转移到交易关闭
)

但是status在已经序列化JSON格式中为”1”,而不是可读的汉子,因此我还需要针对该字段进行自定义序列化。

具体的方法有两种:


法一:
1
2
3
4
5
6
7
8
9
10
11

class Order_basicSerializer(serializers.ModelSerializer):
"""订单序列化容器"""

order_details = serializers.SerializerMethodField() # 递归嵌套序列化器,订单细节

status = serializers.SerializerMethodField() # 获取可读的status

def get_status(self, obj):
return obj.get_status_display()

说明:同样采用serializers.SerializerMethodField()方法指定字段,然后通过get_status方法获取可读字段。这种方法适用于不同的序列化器中,比较灵活,但如果多个序列化器都要获取某一个字段的可读形式,代码量就比较多了。因此还有第二种方法。


法二:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class ChoiceDisplayField(serializers.ChoiceField):

def to_representation(self, value):
"""针对value(choice)转成我们需要的格式"""
# value为从数据库中读出来的序列化之前的值,比如“1”
return self.choices[value]

class Order_basicSerializer(serializers.ModelSerializer):
"""订单序列化容器"""
# 订单状态
status_choice = (
("1", '代付款'), # 用户提交订单,尚未付款,此时会锁定库存
("2", '代发货'), # 用户付款后,等待商家接单前
("3", '交易成功'), # 用户确认收货之后,订单完成交易
("4", '已取消'), # 付款前取消订单
("5", '售后中'), # 商家发货或付款后,用户取消订单
("6", '交易关闭'), # 取消订单或售后结束都转移到交易关闭
)
status = ChoiceDisplayField(choices=status_choice)

说明:这种方法通过自定义序列化,获取choice中的可读字段值。这种方法好处在于自定义的序列化类可以重用,但是需要冗余选择项,类似status_choice,特别是在继承了serializers.ModelSerializer后。

这样:经过序列化后,就可以返回可读的status。

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
[
{
"order_id": 1,
"trade_number": "102301230213021",
"total_price": "9999.0",
"commodity_total_counts": 100,
"generate_time": "2020-05-26T15:07:08",
"status": "代付款",
"order_details": [
{
"price": "50.00",
"commodity": [
{
"store_name": "店铺1",
"commodity_name": "貂皮大衣",
"intro": "舒适的不要不要的",
"category": "衣服",
"discounts": false,
"freight": 0,
"image": null
}
],
"commodity_counts": 25
},
{
"price": "100.00",
"commodity": [
{
"store_name": "店铺1",
"commodity_name": "貂皮大衣",
"intro": "舒适的不要不要的",
"category": "衣服",
"discounts": false,
"freight": 0,
"image": null
}
],
"commodity_counts": 25
}
]
}