django-rest-framework(概念篇)——seri
https://juejin.im/post/5a68934551882573443cddf8
Serializer的作用:
- 将queryset与model实例等进行序列化,转化成json格式,返回给用户(api接口)
- 将post与patch/put的上来的数据进行验证
- 对post与patch/put数据进行处理
简单来说,针对get来说,serializers的作用体现在第一条,但如果是其他请求,serializers能够发挥2,3条的作用。
Save Instance
如果只是简单的get请求,那么在设置了前面的field可能就能够满足这个需求。
我们在view以及mixins的博客中提及到,post请求对应create方法,而patch请求对应update方法,这里提到的create方法与update方法,是指mixins中特定类中的方法。我们看一下源代码,源代码具体分析可以看到另外一篇博客mixins:
# 只截取一部分
class CreateModelMixin(object):
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def perform_create(self, serializer):
serializer.save()
class UpdateModelMixin(object):
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
if getattr(instance, '_prefetched_objects_cache', None):
# If 'prefetch_related' has been applied to a queryset, we need to
# forcibly invalidate the prefetch cache on the instance.
instance._prefetched_objects_cache = {}
return Response(serializer.data)
def perform_update(self, serializer):
serializer.save()
可以看出,无论是create与update都写了一行:serializer.save( ),那么,这一行,到底做了什么事情,分析一下源码。
# serializer.py
def save(self, **kwargs):
# 略去一些稍微无关的内容
···
if self.instance is not None:
self.instance = self.update(self.instance, validated_data)
···
else:
self.instance = self.create(validated_data)
···
return self.instance
显然,serializer.save的操作,它去调用了serializer的create或update方法,不是mixins中的!!!我们看一下流程图(以post为例)
CreateModelMixins执行逻辑如果你的viewset含有post,那么你需要重载create方法,如果含有patch,那么就需要重载update方法。
Validation自定义验证逻辑
- 单独的Validate
mobile_phone = serializers.CharField(max_length=11, min_length=11)
def validate_mobile_phone(self, mobile_phone):
# 注意参数,self以及字段名
# 注意函数名写法,validate_ + 字段名字
if not re.match(REGEX_MOBILE, mobile):
# REGEX_MOBILE表示手机的正则表达式
raise serializers.ValidationError("手机号码非法")
return mobile_phone
- 联合Validate
start = serializers.DateTimeField()
finish = serializers.DateTimeField()
def validate(self, attrs):
# 传进来什么参数,就返回什么参数,一般情况下用attrs
if attrs['start'] > attrs['finish']:
raise serializers.ValidationError("finish must occur after start")
return attrs
这个方法非常的有用,我们还可以再这里对一些read_only的字段进行操作,我们在read_only提及到一个例子,订单号的生成,我们可以在这步生成一个订单号,然后添加到attrs这个字典中。
order_sn = serializers.CharField(readonly=True)
def validate(self, attrs):
# 调用一个方法生成order_sn
attrs['order_sn'] = generate_order_sn()
return attrs
这个方法运用在modelserializer中,可以剔除掉write_only的字段,这个字段只验证,但不存在与指定的model当中,即不能save( ),可以在这delete掉!
def validate(self, attrs):
del attrs["code"]
return attrs
- Validator
validators可以直接作用于某个字段,这个时候,它与单独的validate作用差不多
def multiple_of_ten(value):
if value % 10 != 0:
raise serializers.ValidationError('Not a multiple of ten')
class GameRecord(serializers.Serializer):
score = IntegerField(validators=[multiple_of_ten])
当然,drf提供的validators还有很好的功能:UniqueValidator,UniqueTogetherValidator等。
UniqueValidator: 指定某一个对象是唯一的,如,用户名只能存在唯一:
username = serializers.CharField(
max_length=11,
min_length=11,
validators=[UniqueValidator(queryset=UserProfile.objects.all())
)
UniqueTogetherValidator: 联合唯一,如用户收藏某个课程,这个时候就不能单独作用于某个字段,我们在Meta中设置。
class Meta:
validators = [
UniqueTogetherValidator(
queryset=UserFav.objects.all(),
fields=('user', 'course'),
message='已经收藏'
)]
ModelSerializer
ModelSerializer在Meta中设置fields字段,系统会自动进行映射,省去每个字段再写一个field。
class UserDetailSerializer(serializers.ModelSerializer):
"""
用户详情序列化
"""
class Meta:
model = User
fields = ("name", "gender", "birthday", "email", "mobile")
# fields = '__all__': 表示所有字段
# exclude = ('add_time',): 除去指定的某些字段
# 这三种方式,存在一个即可
ModelSerializer需要解决两个问题
- 某个字段不属于指定model,它是write_only,需要用户传进来,但我们不能对它进行save( ),因为ModelSerializer是基于Model,这个字段在Model中没有对应,这个时候,我们需要重载validate!
如在用户注册时,我们需要填写验证码,这个验证码只需要验证,不需要保存到用户这个Model中:
- 某个字段不属于指定model,它是write_only,需要用户传进来,但我们不能对它进行save( ),因为ModelSerializer是基于Model,这个字段在Model中没有对应,这个时候,我们需要重载validate!
def validate(self, attrs):
del attrs["code"]
return attrs
- 某个字段不属于指定model,它是read_only,只需要将它序列化传递给用户,但是在这个model中,没有这个字段!我们需要用到SerializerMethodField。
假设需要返回用户加入这个网站多久了,不可能维持这样加入的天数这样一个数据,一般会记录用户加入的时间点,然后当用户获取这个数据,我们再计算返回给它。
- 某个字段不属于指定model,它是read_only,只需要将它序列化传递给用户,但是在这个model中,没有这个字段!我们需要用到SerializerMethodField。
class UserSerializer(serializers.ModelSerializer):
days_since_joined = serializers.SerializerMethodField()
# 方法写法:get_ + 字段
def get_days_since_joined(self, obj):
# obj指这个model的对象
return (now() - obj.date_joined).days
class Meta:
model = User
关于外键的Serializers
假设现在有一门课python入门教学(course),它的类别是python(catogory)。
ModelSerializer直接通过映射就可以获得的一个外键类别的id,但并不能获取到外键Model实例详细的信息,如果想要获取到具体信息,那需要嵌套serializer
category = CourseCategorySerializer()
以上外键都是正向取得,下面介绍怎么反向去取,如,我们需要获取python这个类别下有什么课程。首先,在课程course的model中,需要在外键中设置related_name:
class Course(model.Model):
category = models.ForeignKey(CourseCategory, related_name='courses')
# 反向取课程,通过related_name
# 一对多,一个类别下有多个课程,一定要设定many=True
courses = CourseSerializer(many=True)