DRF 外键字段深度查询优化、ListSerializer辅助完成群改
一、Response封装
用def自带的response模块返回数据需要data和status等参数,就算有相同的response也需要重新写一遍,这样显得很不方便。
因此我们可以将response模块进行二次封装,减少一些代码的重复书写,这样就可以使我们的工作量变小。
方法:
在应用文件夹中重新写一个response.py
文件,专门用来二次封装drf的response模块。
class APIResponse(Response):
def __init__(self, status=0, msg='ok', results=None, http_status=None,headers=None, exception=False, content_type=None, **kwargs):
# 将status、msg、results、kwargs格式化成data
data = {
'status': status,
'msg': msg,
}
# results只要不为空都是数据:False、0、'' 都是数据 => 条件不能写if results
if results is not None:
data['results'] = results
# 将kwargs中额外的k-v数据添加到data中
data.update(**kwargs)
super().__init__(data=data, status=http_status, headers=headers, exception=exception, content_type=content_type)
二、外键字段深度查询
1、序列化配置exclude、depth
必须有子序列化类配合,不能反序列化了。
-
只能在序列化中使用。
-
字段名必须是外键(正向反向)字段。
- 因为相对于自定义序列化外键字段,自定义序列化字段是不能参与反序列化的,而子序列化必须为外键名,所以就无法入库。
-
在外键关联数据是多条时,需要明确many=True。
-
是单向操作,因为作为子系列的类必须写在上方,所以不能产生逆方向的子序列化。
class PublishModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Publish
# fields = ['name', 'address', 'books']
# 了解配置
# fields = '__all__' # 所有字段
exclude = ['name'] # 排除字段
depth = 2 # 自动深度,值代表深度次数,但是被深度的外键采用__all__,显示所有字段
2、模型层函数、插拔式字段查询
设置模型表中需要进行序列化和反序列化的字段(容器,列表或元组)。
并且容器中的名称可以映射到模型类中不存在任何参数的属性或方法,通过这种特性可以完成自定义字段的插拔式设计。
# models.py
class Car(models.Model):
name = models.CharField(max_length=64,unique=True)
price = models.DecimalField(max_digits=10,decimal_places=2)
image = models.ImageField(upload_to='img',default='img/default.jpg')
brand = models.CharField(max_length=32,default='unknown')
@property # 可以不写
def image_path(self): # 该方法不能携带参数
return f'{settings.BASE_URL}{settings.MEDIA_URL}{self.image}'
# serializer.py
class CarModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Car
fields = [
'name','price','image_path','brand'
]
三、listserializer辅助类
ListSerializer
类能够序列化和一次验证多个对象。你通常不需要直接使用ListSerializer
,而是应该在实例化一个序列化器时简单地传递一个many=True
参数。
当一个序列化器在带有many=True
选项被序列化时,将创建一个ListSerializer
实例。该序列化器类将成为ListSerializer
类的子类。
当你要自定义多个对象的更新行为(群改)时,你需要手动定制ListSerializer
类的一些行为。
可以通过使用序列化器类的Meta
类下面的list_serializer_class
选项来修改当many=True
时正在使用的类。
# 多表操作
class CarListSerializer(serializers.ListSerializer):
# 自定义的群增群改辅助类,没有必要重写create方法
def create(self, validated_data):
return super().create(validated_data)
def update(self, instance_list, validated_data_list):
return [
self.child.update(instance_list[index], attrs) for index, attrs in enumerate(validated_data_list)
]
# 汽车表序列化
class CarModelSerializer(serializers.ModelSerializer):
# 通过SerializerMethodField设置的字段为只读字段
car_color = serializers.SerializerMethodField()
def get_car_color(self,obj):
return obj.get_color_display()
re_brand = serializers.CharField(
write_only=True,
error_messages={
'required':'re_brand为必填字段!'
}
)
class Meta: # 声明
# 绑定需要进行序列化和反序列化的模型表
model = models.Car
# 设置模型表中需要进行序列化和反序列化的字段(容器,列表或元组)。
# 并且容器中的名称可以映射到模型类中不存在任何参数的属性或方法,
# 通过这种特性可以完成自定义字段的插拔式设计
fields = [
'name','car_color','price','image_path','brand','re_brand'
]
read_only_fields = ['car_color','image_path']
# 设置额外的序列化与反序列化的规则
extra_kwargs = {
'name':{
'min_length':2,
'max_length':10,
'error_messages':{
'min_length': '太短',
'max_length': '太长',
}
}
}
def validate(self, attrs):
brand = attrs.get('brand')
re_brand = attrs.pop('re_brand',False)
if not brand == re_brand:
raise serializers.ValidationError({'re_brand':'两次品牌不一致!'})
return attrs