rest_framework学习(二)序列化类的使用

序列化方式

方式一

from rest_framework.views import APIView
from django.shortcuts import render, HttpResponse
import json
class PublishView(APIView):
    def get(self, request):
        publish_list = list(Publish.objects.all().values('title', 'email'))
        return HttpResponse(json.dumps(publish_list))

方式二

from django.core import serializers
from rest_framework.views import APIView
from django.shortcuts import render, HttpResponse
class PublishView(APIView):
    def get(self, request):
        publish_list = Publish.objects.all()
        ret = serializers.serialize('json', publish_list)
        return HttpResponse(ret)

方式三

from rest_framework.views import APIView
from rest_framework.response import Response
from app01.Serializers import *
class PublishView(APIView):
    def get(self, request):
        publish_list = Publish.objects.all()
        ret = PublishSerializer(publish_list, many=True)
        return Response(ret.data)

app01.Serializers.py

from rest_framework import serializers
class PublishSerializer(serializers.Serializer):
    title = serializers.CharField()
    email = serializers.CharField()

 方式四

from rest_framework.views import APIView
from rest_framework.response import Response
from app01.Serializers import *
class PublishView(APIView):
    def get(self, request):
        publish_list = Publish.objects.all()
        ret = PublishModelSerializer(publish_list, many=True)
        return Response(ret.data)

app01.Serializers.py

from rest_framework import serializers
class PublishModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Publish
        fields = "__all__"

详细介绍方式四

继承serializers.ModelSerializer类,需要在序列化类中定义Meta类

Meta常用的参数

model:需要被序列化的类
list_serializer_class:批量序列化时调用的序列化类,在调用序列化类传入多个对象组成的列表或集合时,需要传入关键字参数many=True,将调用这个类。一般不需要配置这个类
fields:需要被序列化的字段组成的列表或元祖,值为"__all__"时表示全部字段
exclude:不被序列化的字段主城的列表或元祖,与fields不能同时用

使用方法

class Meta:
    model = Dynamic
    list_serializer_class = DynamicsListSerializer # 另一个序列化类
    # fields="__all__"  # 不能与exclude 一起用
    exclude = ["字段一", "字段二", "字段三"]

serializers.SerializerMethodField()

序列化的结果中需要有模型类中没有的字段时,将会使用到这个对象,需要重写个钩子方法。

使用方法

gender = serializers.SerializerMethodField()
def get_gender(self, obj):
    '''
    配合gender定义的钩子方法,方法名必须是get_自定义字段名,参数obj是Meta中model指定类的实例对象
    '''
    return obj.user.gender

 序列化类做数据校验

在收到前端传到后台的数据后,可以使用序列化类进行数据校验。

user = serializers.CharField(source='user.pk', required=False, allow_blank=True, error_messages={
    'required': "User value can not be empty",
})

序列化类使用这种方式时,既可以对数据做序列化也可以对数据进行校验

例如这里的user字段,原本的模型类里的user字段作为外键关联到另外一个类,如果不做处理,直接序列化,序列化结果中user字段的值将是user模型类的__str__()方法的返回值。声明参数source='user.pk',序列化的结果将是user的主键值。

required的值是True时,将会对该字段为必须参数,False允许不传

allow_blank值为True,该字段的值可以为空字符串,默认为False

error_messages值是字典类型,key为该字段的约束名,value为违反约束后的提示信息

phone = serializers.CharField(max_length=11, required=True, error_messages={
        'max_length': 'Phone number length exceeds 11',
        'required': "Phone value can not be empty"
    })

此外,还可以使用钩子方法校验数据

局部钩子

    def validate_phone(self, attrs):
        r = re.compile(r"^1((3[0-9])|(4[5,7])|(5[0-3,5-9])|(7[0,3,5-8])|(8[0-9])|66|98|99|47)\d{8}")
        if not isinstance(attrs, str):
            raise exceptions.ValidationError("Phone number type error")
        if not r.match(attrs):
            raise exceptions.ValidationError("Phone number is error")
        return attrs

这个示例钩子方法会验证模型类中的phone字段,参数attrs即前端传入的phone字段的值,如果通过校验则返回该值,否则主动抛出exceptions.ValidationError异常

全局钩子

    def validate(self, attrs):
        if not attrs['detail'] and not attrs['photos']:
            raise exceptions.ValidationError("Detail and Photos cannot be empty at the same time")
        return attrs

全局钩子方法是在所有局部钩子运行完毕后才运行的方法,参数attrs是字典型的全部需要校验的数据,如果通过校验则返回attrs,否则主动抛出exceptions.ValidationError异常

有时候未完成自定义功能需要重写父类的方法,如现有一个需求需要将前端传入过来的的多个图片链接拼接成字符串存入数据库,且图片链接用","隔开。

对于接收前端传过来的图片链接组成的列表可以使用

photos = serializers.ListField(required=True, error_messages={
    'required': "Photos value can not be empty"
})

但是想要序列化该类的对象,却可能会发现序列化后的数据中photos字段对应的也是一个列表,但是这个列表很特殊,使用''.join(photos )拼接后正好是数据库中存的图片链接拼接后的字符串。对于这种数据前端是无法直接使用的。为了解决这个问题,可以重写父类的data方法。

    @property
    def data(self):
        ret = super(CircleUserSerializer, self).data
        if isinstance(ret, dict):
            ret['photos'] = ''.join(ret['photos']).split(',')
        return ret

为了能批量序列化该类,需要重新写一个继承serializers.ListSerializer的序列化类

class CircleUserListSerializer(serializers.ListSerializer):
    @property
    def data(self):
        ret = super(CircleUserListSerializer, self).data
        for _ in ret:
            _['photos'] = ''.join(_['photos']).split(',')
        return ret

因为序列化对象需要调用data方法返回序列化数据,所以重写data方法是最简单的解决办法。

在使用序列化类的反序列化功能时,有时候遇到模型类中有多对多映射或一对多映射的字段时,默认的序列化类将不能根据传入的数据实例化成对象。解决这个问题就需要重写serializers.ModelSerializer类的create方法。

    def create(self, validated_data):
        photos=validated_data.pop('photos')
        dynamic= Dynamic.objects.create(**validated_data)
        for photo in photos:
            Photo.objects.create(src=photo,dynamic=dynamic)
        return dynamic

下面是部分实例代码

from rest_framework import serializers
from rest_framework import exceptions
from .models import CircleUser



class CircleUserListSerializer(serializers.ListSerializer):
    @property
    def data(self):
        ret = super(CircleUserListSerializer, self).data
        for _ in ret:
            _['photos'] = ''.join(_['photos']).split(',')
        return ret


class CircleUserSerializer(serializers.ModelSerializer):
    class Meta:
        model = CircleUser
        list_serializer_class = CircleUserListSerializer
        # fields = "__all__"
        exclude = ['app_id', 'session_key']

    phone = serializers.CharField(max_length=11, required=True, error_messages={
        'max_length': 'Phone number length exceeds 11',
        'required': "Phone value can not be empty"
     })
    career = serializers.CharField(max_length=128, required=True, error_messages={
        'max_length': "Career value length exceeds 128",
        'required': "Career value can not be empty"
    })
    child_introduction = serializers.CharField(max_length=140, required=True,
                                               error_messages={
                                                   'max_length': "Child_introduction value length exceeds 140",
                                                   'required': "Child_introduction value can not be empty"
                                               })
    spouse_description = serializers.CharField(max_length=140, required=False, allow_blank=True,
                                               error_messages={
                                                   'max_length': "Spouse_description value length exceeds 140",
                                               })

    photos = serializers.ListField(required=True, error_messages={
        'required': "Photos value can not be empty"
    })

    def validate_phone(self, attrs):
        r = re.compile(r"^1((3[0-9])|(4[5,7])|(5[0-3,5-9])|(7[0,3,5-8])|(8[0-9])|66|98|99|47)\d{8}")
        if not isinstance(attrs, str):
            raise exceptions.ValidationError("Phone number type error")
        if not r.match(attrs):
            raise exceptions.ValidationError("Phone number is error")
        return attrs
    
    def validate_career(self, attrs):
        if not isinstance(attrs, str):
            raise exceptions.ValidationError("Career value type error")
        return attrs
    
    def validate_child_introduction(self, attrs):
        if not isinstance(attrs, str):
            raise exceptions.ValidationError("Child_introduction value type error")
        return attrs

    def validate_photos(self, attrs):
        if not isinstance(attrs, list):
            raise exceptions.ValidationError("Photos value type error")
        if not attrs:
            raise exceptions.ValidationError("Photos value can not be empty")
        url_complie = re.compile(r'(http|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?')
        for _ in attrs:
            if not url_complie.match(_):
                raise exceptions.ValidationError("Photo url is error")
        return attrs

    @property
    def data(self):
        ret = super(CircleUserSerializer, self).data
        if isinstance(ret, dict):
            ret['photos'] = ''.join(ret['photos']).split(',')
        return ret

 

posted @ 2019-07-02 23:03  Wuliwawa  阅读(120)  评论(0编辑  收藏  举报