django rest framework serializers小结

引言

serializers是什么?

官网是这样的”Serializers allow complex data such as querysets and model instances to be converted to native Python datatypes that can then be easily rendered into JSON, XML or other content types. “翻译出来就是,将复杂的数据结构变成json或者xml这个格式的。

  • 将queryset与model实例等进行序列化,转化成json格式,返回给用户(api接口)。
  • 将post与patch/put的上来的数据进行验证。
  • 对post与patch/put数据进行处理。

简单来说,针对get来说,serializers的作用体现在第一条,但如果是其他请求,serializers能够发挥2,3条的作用!

本文比较长,用张图简单介绍一下主要内容

 

1. 常用的field

 
CharField、BooleanField、IntegerField、DateTimeField这几个用得比较多,我们把外键的field放到后面去说!
 
例如:
  • mobile = serializers.CharField(max_length=11, min_length=11)
  • age = serializers.IntegerField(min_value=1, max_value=100)
 
# format可以设置时间的格式,下面例子会输出如:2018-1-24 12:10
  • pay_time = serializers.DateTimeField(read_only=True,format='%Y-%m-%d %H:%M')
  • is_hot = serializers.BooleanField()
  • HiddenField

HiddenField的值不依靠输入,而需要设置默认的值,不需要用户自己post数据过来,也不会显式返回给用户,最常用的就是user!!

我们在登录情况下,进行一些操作,假设一个用户去收藏了某一门课,那么后台应该自动识别这个用户,然后用户只需要将课程的id post过来,那么这样的功能,我们配合CurrentUserDefault()实现。
# 这样就可以直接获取到当前用户

user = serializers.HiddenField(default=serializers.CurrentUserDefault()
 
不同的是,我们在django中,form更强调对提交的表单进行一种验证,而serializer的field不仅在进行数据验证时起着至关重要的作用,在将数据进行序列化后返回也发挥着重要作用!!

 

2. Core arguments参数

 
read_only:True表示不允许用户自己上传,只能用于api的输出。如果某个字段设置了read_only=True,那么就不需要进行数据验证,只会在返回时,将这个字段序列化后返回
 
write_only: 与read_only对应相反;必须上传,不返回。
 
required: 顾名思义,就是这个字段是否必填。
 
allow_null/allow_blank:是否允许为NULL/空 。
 
error_messages:出错时,信息提示。
name = serializers.CharField(required=True, min_length=6,
                error_messages={'min_length': '名字不能小于6个字符','required': '请填写名字'})

 

label: 字段显示设置,如 label=’验证码’
 
help_text: 在指定字段增加一些提示文字,这两个字段作用于api页面比较有用

style: 说明字段的类型,这样看可能比较抽象,看下面例子:
# 在api页面,输入密码就会以*显示
password = serializers.CharField(style={'input_type': 'password'})

# 会显示选项框
color_channel = serializers.ChoiceField(choices=['red', 'green', 'blue'],
                                        style={'base_template': 'radio.html'})
 
这里面,还有一个十分有用的validators参数,这个我们会在后面提及!

 

save instance

我们在view以及mixins的博客中提及到,post请求对应create方法,而patch请求对应update方法,这里提到的create方法与update方法,是指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为例)
  讲了那么多,我们到底需要干什么!重载这两个方法!!
 
  如果你的viewset含有post,那么你需要重载create方法,如果含有patch,那么就需要重载update方法。

 
# 假设现在是个博客,有一个创建文章,与修改文章的功能, model为Article。

class ArticleSerializer(serializers.Serializer):
    user = serializers.HiddenField(
        default=serializers.CurrentUserDefault())
    name = serializers.CharField(max_length=20)
    content = serializers.CharField()

    def create(self, validated_data):
    # 除了用户,其他数据可以从validated_data这个字典中获取
    # 注意,users在这里是放在上下文中的request,而不是直接的request(view视图中是self.request.user)
    #super().create(**validated_data)
    
        user = self.context['request'].user
        name = validated_data['name ']
        content = validated_data['content ']
        return Article.objects.create(**validated_data)

    def update(self, instance, validated_data):
    # 更新的特别之处在于你已经获取到了这个对象instance
    #super().update(instance, validated_data)
    
        instance.name = validated_data.get('name')
        instance.content = validated_data.get('content')
        instance.save()
        return instance
 
 可能会有人好奇,系统是怎么知道,我们需要调用serializer的create方法,还是update方法,我们从save( )方法可以看出,判断的依据是:
if self.instance is not None:
    pass

 

那么我们的mixins的create与update也已经在为开发者设置好了。
# CreateModelMixin
serializer = self.get_serializer(data=request.data)


# UpdateModelMixin
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)

 

也就是说,在update通过get_object( )的方法获取到了instance,然后传递给serializer,serializer再根据是否有传递instance来判断来调用哪个方法!

 
 

validate方法

常用验证方式:如果是两个字段联合在一起进行验证,那么我们就可以重载validate( )方法
start = serializers.DateTimeField()
finish = serializers.DateTimeField()

def validate(self, attrs):
    #attrs:前端传递过来的参数,组成一个类似字典的数据结构
    # 传进来什么参数,就返回什么参数,一般情况下用attrs
    if data['start'] > data['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掉!
如在用户注册时,我们需要填写验证码,这个验证码只需要验证,不需要保存到用户这个Model中:
def validate(self, attrs):
    del attrs["code"]
    return attrs

除了validate验证字段集合,也能通过validate_field来验证字段,这里不做说明,建议使用上方的validate方法

 
 

ModelSerializer 

讲了很多Serializer的,在这个时候,我还是强烈建议使用ModelSerializer,因为在大多数情况下,我们都是基于model字段去开发。
好处:
 
ModelSerializer已经重载了create与update方法,它能够满足将post或patch上来的数据进行进行直接地创建与更新,除非有额外需求,那么就可以重载create与update方法。
 
ModelSerializer在Meta中设置fields字段,系统会自动进行映射,省去每个字段再写一个field
 
class UserDetailSerializer(serializers.ModelSerializer):
    """
    用户详情序列化
    """

    class Meta:
        model = User
        fields = ("name", "gender", "birthday", "email", "mobile") #选择字段
        # fields = '__all__': 表示所有字段
        # exclude = ('add_time',):  除去指定的某些字段
        # 这三种方式,存在一个即可
 

自定义显示字段

某个字段不属于指定model,它是read_only,只需要将它序列化传递给用户,但是在这个model中,没有这个字段!我们需要用到SerializerMethodField。自定义显示字段
class UserSerializer(serializers.ModelSerializer):  
    #自定义显示字段
    days_since_joined = serializers.SerializerMethodField()
    
    # 方法写法:get_ + 字段
    def get_days_since_joined(self, obj):
    # obj指这个model的对象,既user对象
        return (now() - obj.date_joined).days

    class Meta:
        model = User 
当然,这个的SerializerMethodField用法还相对简单一点,后面还会有比较复杂的情况

 

 

serializers外键 

其实,外键的field也比较简单,如果我们直接使用serializers.Serializer,那么直接用PrimaryKeyRelatedField就解决了。
 
假设现在有一门课python入门教学(course),它的类别是python(catogory)。
#model
class CourseCategory(model.Model):
    name = model.CharField(max_length=255)
    
class Course(model.Model):
    name = model.CharField(max_length=255)
    category = models.ForeignKey(CourseCategory,on_delete=models.CASCADE,related_name='courses')  

# 指定queryset category = serializers.PrimaryKeyRelatedField(queryset=CourseCategory.objects.all(), required=True)

 
ModelSerializer就更简单了,直接通过映射就好了

不过这样只是用户获得的只是一个外键类别的id,并不能获取到详细的信息,如果想要获取到具体信息,那需要嵌套serializer
class CourseCategorySerializers(serializers.ModelSerializer):
    courses = CourseSerializers(many=True)  #反向取值  #2
    class Meta:
        model = CourseCategory
        fileds = "__all__"

class CourseSerializers(serializers.ModelSerializer):
    category = CourseCategorySerializer() #正向取值  #1

    class Meta:
        model = Course 
        fileds = "__all__"


正向取值:课程取分类
#1处
category = CourseCategorySerializer() #正向取值


分类反向取课程,通过related_name
#2处
在课程course的model中,需要在外键中设置related_name
一对多,一个类别下有多个课程,一定要设定many=True
courses = CourseSerializer(many=True)

  

 

posted @ 2020-05-04 20:19  小龙1996  阅读(188)  评论(0编辑  收藏  举报