rest_framework之序列化组件

什么是rest_framework序列化?

在写前后端不分离的项目时:

  我们有form组件帮我们去做数据校验

  我们有模板语法,从数据库取出的queryset对象不需要人为去转格式

当我们写前后端分离项目的时:

  我们需要自己去做数据校验

  我们需要手动去转数据格式,因为跨平台数据传输都用json字符串,不能直接jsonqueryset对象

这个时候你就要想想了,原生django里有这么神奇的组件,那rest_framework里会不会也有这样的组件呢?嗯...果然有!

这就是rest_framework的序列化组件,下面我们一起来看!

怎么用rest_framework的序列化组件?

首先我们需要从rest_framework导几个模块

from rest_framework.serializers import Serializer,ModelSerializer
from rest_framework import serializers
Serializer是rest_framework原生的序列化组件
ModelSerializer是rest_framework在原生的序列化组件的基础上封装了一层的序列化组件

用法:1、在用我们的rest_framework序列化组件的时候,我们的视图层都必须写视图类,不能再写视图函数

   2、我们需要针对每一张模型表写一个类来继承Serailizer或者ModelSerailizer类,

    当我们在视图类里需要对数据进行序列化或者反序列化的时候,在自己定义的类传入需要序列化的数据实例化,调用.data即可拿到序列化或者校验后的数据了

原生Serializer用法:

详细使用我们来上代码,我们起一个django项目,在app01里面新建myserializer文件,这里面写我们定义的序列化类。

看项目应用目录

路由层

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # /books/的时候表示获取所有图书, /book/1/ 表示对id为1的图书进行操作
    url(r'^book/(?P<id>w+)/$|(?P<type>books)/$', views.Book.as_view()),

    # 获取所有出版社信息
    url(r'^publish/$', views.Publishs.as_view()),

    # 对某个出版社进行操作
    url(r'^publish/(?P<id>w+)/$', views.Publish.as_view()),

    # 作者author部分暂没写
]

模型层

from django.db import models

# Create your models here.
from django.contrib.auth.models import User,AbstractUser


class UserInfo(AbstractUser):
    phone = models.CharField(max_length=15)
    avatar = models.FileField(upload_to='static/avatar',default=None)


class Book(models.Model):
    title = models.CharField(max_length=64)
    price = models.DecimalField(max_digits=10,decimal_places=2)
    publish_time = models.DateField(null=True)
    authors = models.ManyToManyField(to='Author')
    publish = models.ForeignKey(to="Publish")
    def __str__(self):
        return self.title


class Author(models.Model):
    name = models.CharField(max_length=64)
    choices = ((0, ''), (1, ''), (2, '保密'))
    sex = models.IntegerField(choices=choices)
    info = models.CharField(null=True,max_length=255)

    def __str__(self):
        return self.name


class Publish(models.Model):
    name = models.CharField(max_length=32)
    phone = models.CharField(null=True,max_length=15)
    address = models.CharField(null=True,max_length=255)

    def __str__(self):
        return self.name

 序列化组件层

from rest_framework import serializers
from app01 import models

"""
使用步骤:
1、新建序列化类,继承Serializer
2、类中定义和模型表一一对应的字段
    -其中类中的名字可以改变,需要在serializers.CharField()的括号中指定source=某个字段,建议映射关系
    -外键关系的字段可以用serializers.SerializerMethodField(),需要在下方固定写 get_字段名 的方法,
        这里可以写具体逻辑,最终返回结果就是该字段的结果
3、当新增数据的时候需要重写父类的create方法,逻辑由自己实现,可以参考下面BookSerilizers类中实现的create方法
4、当修改数据的时候需要重写父类的update方法,逻辑由自己实现,可以参考下面BookSerilizers类中实现的update方法
5、当完成这些配置后就可以在视图类中实例化调用了,序列化的时候序列化,反序列化的时候校验
"""


class PublishSerilizers(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    name = serializers.CharField(max_length=32)
    phone = serializers.CharField(max_length=15)
    address = serializers.CharField(max_length=255)


class AuthorSerilizers(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    name = serializers.CharField(max_length=32)
    sex = serializers.CharField() 
    info = serializers.CharField(max_length=255)


class BookSerilizers(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)   #read_only 是指当前字段只读,前端可以不用传  write_only是指不给前端返回这个字段,但是前端新增和修改必须传
    title = serializers.CharField(max_length=64)
    price = serializers.DecimalField(max_digits=8,decimal_places=2)
    publish_time = serializers.DateField()
    # publish = serializers.CharField(source="publish.name") # source 参数可以指定当下显示的字段名关联至模型表里的哪个字段名,当指定了以后当前命名的名字就不能和数据库里相应的字段名相同了
    publish = serializers.SerializerMethodField(allow_null=True)  # SerializerMethodField的方式,然后下面定义get_字段名的方法,即可在方法体写逻辑代码返回你希望得到值
    def get_publish(self,obj):  # 这里的obj必传,obj是当前循环到的数据对象
        res = PublishSerilizers(instance=obj.publish)  # 序列化当前数据对象关联的publish表里的相应数据记录
        return res.data

    authors = serializers.SerializerMethodField(allow_null=True)
    def get_authors(self,obj):
        authors_list = obj.authors.all()  # author与book是多对多关系,所以需要.all()取到所有的记录,就是ORM的方法
        res = AuthorSerilizers(instance=authors_list,many=True)  #由于是序列化多条记录,指定many=True
        return res.data

    def create(self, validated_data):
        ret = models.Book.objects.create(**validated_data)  # 用Django ORM的操作将视图层传来的数据新增到数据库
        return ret

    def update(self, instance, validated_data):  # instance是数据库原来的对象    validated_data是个字典,装的是校验完成后的需要修改的数据
        instance.name = validated_data.get("name")
        instance.price = validated_data.get("price")
        instance.publish_time = validated_data.get("publish_time")
        instance.publish_id = validated_data.get("publish_id")
        instance.save()     # 逐个替换后保存
        instance.authors.set(validated_data.get("authors")) # 修改多对多表记录,修改需要用set([1,2,4])的形式
        return instance

 视图层

from django.shortcuts import render,HttpResponse

# Create your views here. 以Book信息为例

from rest_framework.views import APIView,Response
from app01 import models
from app01 import myserializers
from app01 import mymodelSer
class  Book(APIView):
    def get(self,request,*args,**kwargs):
        if kwargs.get("type") == 'books': # 根据路由请求过来的方式来判断是查询所有书籍信息还是一本书的信息,这里是查所有的
            books = models.Book.objects.all()
            many= True          # 指定序列化参数,如果是序列化多条数据就是True
        elif kwargs.get("id").isdigit():   # 如果查询携带id,那么就查单本书的
            books = models.Book.objects.filter(id=kwargs.get("id")).first() #序列化单条数据的数据要.first()
            many = False     # 指定序列化参数,如果是序列化单条数据就是True

        res = myserializers.BookSerilizers(instance=books,many=many)  # 将数据库取的queryset对象传入自己定义的序列化类进行序列化
        return Response(res.data)
        pass

    def post(self,request,**kwargs):
        response = {"status_code":100,"msg":"新增成功!"}
        ret = myserializers.BookSerilizers(data=request.data,many=False)   #反序列化前端传来的数据
        if ret.is_valid():   # 数据校验,当执行这句判断时,才会去做校验
            print(ret.validated_data)   # 校验后的结果,用ret.validated_data,它会过滤掉不属于模型表字段的键值对,包括前端传来的publish,authors,因为在序列化类里面这两个字段用了SerializerMethodField
            ret.validated_data["publish_id"] = request.data.get("publish_id")  #在创建book对象前需要手动叫publish字段的键值对添加进去,然后创建book记录
            res = ret.create(ret.validated_data)  # 这里的create方法调用的是我们在BookSerilizers里面重写的父类的create方法
                                                    # 在create的时候必须重写父类的create方法,不然就只能用Django ORM新增,在这里的create里面实际上也是用ORM的新增
            res.authors.add(*request.data.get("authors"))  #手动用ORM新建多对多第三张表的记录
            res.save()
            response["data"] = myserializers.BookSerilizers(instance=res).data  # res 是数据对象,需要序列化得到数据.data获取
            return Response(response)
        else:
            response["status_code"] = 101
            response['msg'] = '新增失败!'
            response["data"] = ret.errors   # 如果校验不成功,错误信息会自动放在errors里,errors = {"name":"局部错误信息",..."detail":"全局错误信息"}
            return Response(response)
        pass

    def put(self,request,**kwargs):
        response = {"status_code": 200, "msg": "修改成功!"}
        obj = models.Book.objects.filter(id = kwargs.get("id")).first()
        ret = myserializers.BookSerilizers(instance=obj,data=request.data, many=False) # 修改时需要将原数据对象和前端传来的将要修改的信息传入BookSerilizers反序列化校验
        if ret.is_valid(): # 校验成功时
            ret.validated_data['publish_id'] = request.data.get("publish_id")
            ret.validated_data['authors'] = request.data.get("authors")
            res = ret.update(instance=obj,validated_data=ret.validated_data) # 将数据对象和校验后的结果传入更新
            response["data"] = ret.validated_data
            return Response(response)
        else:
            response["data"] = ret.errors
            return Response(response)
        pass

    def delete(self,request):

        pass

 

 Serailizer使用总结:定义序列化类继承Serailizer,类中定义与模型表一一对应的字段,然后再定义局部钩子和全局钩子做校验用,重写create和update方法,最后再视图类中实例化序列化类,通过.data取到序列化后的数据

下面我们来看ModelSerailizer的使用:

路由层

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^publish/$', views.PublishsView.as_view()),
    url(r'^publish/(?P<pk>d+)/$', views.PublishView.as_view()),
    url(r'^book/$', views.BooksView.as_view()),
    url(r'^book/(?P<pk>d+)/$', views.BookView.as_view()),
    url(r'^author/$', views.AuthorsView.as_view()),
    url(r'^author/(?P<pk>d+)/$', views.AuthorView.as_view()),

]

模型层

from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.class Publish(models.Model):
    name = models.CharField(max_length=32)
    phone = models.CharField(max_length=15)
    address = models.CharField(max_length=255)

class Book(models.Model):
    title = models.CharField(max_length=64)
    price = models.DecimalField(max_digits=8,decimal_places=2)
    publish = models.ForeignKey(to='Publish')
    authors = models.ManyToManyField(to='Author')

class Author(models.Model):
    name = models.CharField(max_length=32)
    sex = models.IntegerField(choices=((1,''),(0,''),(2,'保密')),default=1)
    info = models.CharField(max_length=255,default='这人很懒,什么都没写!')

 序列化组件层

from rest_framework.serializers import Serializer,ModelSerializer
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from app01 import models

"""
使用步骤:
1、新建序列化类,继承ModelSerializer
2、类中定义和模型表一一对应的字段,这里可以定义class Meta() 然后指定模型表model和 映射字段fields,比Serializer更简洁
    -其中类中的名字可以改变,需要在serializers.CharField()的括号中指定source=某个字段,建议映射关系
    -外键关系的字段可以用serializers.SerializerMethodField(),需要在下方固定写 get_字段名 的方法,
        这里可以写具体逻辑,最终返回结果就是该字段的结果
3、当新增数据的时候不需要重写父类的create方法,这里ModelSerializer做了封装
4、当修改数据的时候不需要重写父类的update方法,这里ModelSerializer做了封装
5、当完成这些配置后就可以在视图类中实例化调用了,序列化的时候序列化,反序列化的时候校验

"""


class PublishSerializer(ModelSerializer):
    # 自定义序列化类继承ModelSerializer可以在类里面写class Meta()
    class Meta():  # 如果不想每个字段都自己写,那么这就是固定写法,在继承serializer中字段必须自己写,这是二者的区别
        model = models.Publish   # 指定需要序列化的模型表
        fields = ("__all__")   # 指定需要校验的字段  "__all__" 表示所有字段,也可以指定字段(字段一,字段二)

    # exclude = ('name') 和fields用法相反,取除了某个字段以外的字段
    # depth = 1 指定数据跨表深度,2 指跨2两张表,外键关系会取到完整信息
def validate_name(self,value): # 局部钩子 validate_加字段名,需要给定形参,这个形参就是字段值 if value.startswith("sb"): raise ValidationError("出版社名称含有敏感词汇") # 抛出异常,但这个异常是在errors信息中返回给前端,不会在后端报出 return value # 对哪个字段进行校验之后需要将该字段值返回 def validate_phone(self,value): if not value.isdigit(): raise ValidationError("出版社联系方式不合法!") return value def validate(self, attrs): # 全局钩子 做全局性的数据校验,attrs即需要校验的数据{"name":'xxx',...} name = attrs.get("name") phone = attrs.get("phone") address = attrs.get("address") if (not phone) and (not address): raise ValidationError("出版社联系方式和地址必须选填一项") return attrs class BookSerializer(ModelSerializer): class Meta(): model = models.Book fields = ('title','price','authors','publish') #指定序列化的字段 #加上以下这一段在get请求数据的时候会拿到关联字段的详细信息,但是在存的时候会报错 ---方便取,不方便存,存需要另外定义反序列化类 #如果去掉下面这一段在get请求的时候会拿到关联字段的id返回前端,存的时候不会报错 # ======================================================= authors = serializers.SerializerMethodField() def get_authors(self,obj): res = AuthorSerializer(instance=obj.authors.all(),many=True) return res.data publish = serializers.SerializerMethodField() def get_publish(self,obj): res = PublishSerializer(instance=obj.publish) return res.data # ================================================== def validate_title(self,value): # 局部钩子校验 if value.startswith("sb"): raise ValidationError("书名不能包含敏感词汇") return value def validate_price(self,value): # 全局钩子校验 if not value: raise ValidationError("图书价格不能为空!") try: float(value) except ValueError: raise ValidationError("请输入合法的图书价格!") return value class AuthorSerializer(ModelSerializer): class Meta(): model = models.Author fields = ('name','sex','info') # 以下如果自定义sex字段的取值内容,取的时候会取到 '男','女','其他',但存的时候会报错 # 如果不自定义的话,取的是数据库存储的内容 1,0,2 存可以直接通过0,1,2去存,也就是前端只需要传sex的数字值即可存 # ============================================================ sex = serializers.CharField(source="get_sex_display") # ============================================================

视图层

from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from app01 import models
from app01.Object_Seriailzers import PublishSerializer,BookSerializer,AuthorSerializer


# Create your views here.
#出版社接口

class PublishsView(APIView):
    def get(self,request,*args,**kwargs):
        response = {"status_code":100,"msg":"查询成功!",'data':""}
        queryset = models.Publish.objects.all()
        # 对多条数据进行序列化需要制定many=True
        ret = PublishSerializer(instance=queryset,many=True)
        response['data'] = ret.data
        return Response(response)
        pass

    def post(self,request,*args,**kwargs):
        response = {"status_code":200,'msg':"新增成功!","data":''}
        ret = PublishSerializer(data=request.data)
        if  ret.is_valid(): # 这里会执行所有的校验,系统及自定义
            ret.save()  # 这里的保存就会执行数据库的保存,不需要去自己写create方法
            response['data'] = ret.data  #如果是继承原生的Serailizer,获取到过滤后的数据要通过ret.validated_data取
        else:
            response["status_code"] = 201
            response['msg'] = '新增失败!'
            response['data'] = ret.errors
        return Response(response)

        pass

class PublishView(APIView):

    def get(self,request,pk,*args,**kwargs):
        response = {"status_code":100,'msg':'查询成功!','data':''}
        ret = PublishSerializer(models.Publish.objects.filter(pk=pk).first())
        response['data'] = ret.data # 取到序列化后的对象
        return Response(response)
        pass

    def put(self,request,pk,*args,**kwargs):
        response = {"status_code":200,'msg':'修改成功!','data':''}
        # 先查出对象
        query = models.Publish.objects.filter(pk=pk).first()

        # 将数据库对象和前端请求的数据交由PublishSerializer序列化
        ret = PublishSerializer(instance=query,data=request.data)
        if ret.is_valid(): #判断是否校验通过
            # 将数据库对象和前端请求的数据交由update更新,update内部已经封装更新和保存操作
            # 如果涉及到多表更新或其他复杂操作,可以在PublishSerializer中自定义update方法,update返回的是更新后的对象
            data = ret.update(instance=query,validated_data=ret.validated_data)
            # 将修改后的数据返回,注意取ret.data是过滤后的数据,如果是继承原生的Serailizer取过滤后的数据是ret.validated_data取
            response['data'] = ret.data
        else:
            response['status_code'] = 201
            response['msg'] = '修改失败!'
            response['data'] = ret.errors
        return Response(response)
        pass

    def delete(self,request,pk,*args,**kwargs):
        response = {'status_code':200,'msg':'删除成功!'}
        models.Publish.objects.filter(pk=pk).first().delete()
        return Response(response)
        pass

# 图书接口
class BooksView(APIView):

    def get(self, request,*args,**kwargs):
        response = {"status_code":200,'msg':'查询成功!','data':''}
        queryset = models.Book.objects.all()
        ret = BookSerializer(instance=queryset,many=True)
        response['data'] = ret.data
        return Response(response)

        pass

    def post(self, request,*args,**kwargs):
        response = {"status_code": 200, 'msg': '新增成功!', 'data': ''}
        ret = BookSerializer(data=request.data)
        if ret.is_valid():
            obj = ret.save()
            response['data'] = ret.data
        else:
            response['status_code'] = 201
            response['msg'] = '新增失败!'
            response['data'] = ret.errors
        return Response(response)
        pass


class BookView(APIView):

    def get(self, request,pk,*args,**kwargs):
        response = {"status_code": 200, 'msg': '查询成功!', 'data': ''}
        ret = BookSerializer(instance=models.Book.objects.filter(pk=pk).first())
        response['data'] = ret.data
        return Response(response)
        pass

    def put(self, request,pk):
        response = {"status_code": 200, 'msg': '更新成功!', 'data': ''}
        query = models.Book.objects.filter(pk=pk).first()
        ret = BookSerializer(instance=query,data=request.data)
        if ret.is_valid():  # 这里的if也可以不需要,只需要在is_valid()中配置raise_exception=True即可,
                 # 如果校验不通过,内部会直接返回错误信息给前端,下面代码不会走 obj
= ret.save() response['data'] = BookSerializer(instance=obj).data else: response['status_code'] = 201 response['msg'] = '更新失败!' response['data'] = ret.errors return Response(response) pass def delete(self, request,pk): response = {"status_code": 200, 'msg': '删除成功!', 'data': ''} models.Book.objects.filter(pk=pk).first().delete() return Response(response) pass # 作者接口 class AuthorsView(APIView): def get(self,request): response = {"status_code": 200, 'msg': '查询成功!', 'data': ''} queryset = models.Author.objects.all() ret = AuthorSerializer(instance=queryset,many=True) response['data'] = ret.data return Response(response) pass def post(self,request): response = {"status_code": 200, 'msg': '新增成功!', 'data': ''} ret = AuthorSerializer(data=request.data) if ret.is_valid(): obj = ret.save() response['data'] = AuthorSerializer(instance=obj).data else: response['msg'] = '新增失败!' response['data'] = ret.errors return Response(response) pass class AuthorView(APIView): def get(self,request,pk,*args,**kwargs): response = {"status_code": 200, 'msg': '查询成功!', 'data': ''} ret = AuthorSerializer(instance=models.Author.objects.filter(pk=pk).first()) response['data'] = ret.data return Response(response) pass def put(self,request,pk,*args,**kwargs): response = {"status_code": 200, 'msg': '更新成功!', 'data': ''} query = models.Author.objects.filter(pk=pk).first() ret = AuthorSerializer(instance=query, data=request.data) if ret.is_valid(): obj = ret.save() response['data'] = ret.data else: response['status_code'] = 201 response['msg'] = '更新失败!' response['data'] = ret.errors return Response(response) pass def delete(self,request , pk, *args, **kwargs): response = {"status_code": 200, 'msg': '删除成功!', 'data': ''} models.Author.objects.filter(pk=pk).first().delete() return Response(response) pass

ModelSerailizer总结: 在Serailizer的基础上进行了进一步的封装,主要体现在3个地方:

  1、在序列化类中定义字段不需要全部都自己定义映射关系,只需要配置class Meta 里的模型表model 和字段fields

  2、在存和修改的时候不需要在序列化类中实现父类的create和update方法,ModelSerailizer已经做了封装,当然,这是只针对单表的操作,如果涉及多表的操作,我们也可以自己重写create和update方法

  3、在校验数据成功后只需要通过ret.save()即可执行create操作,ret.update即可执行更新操作

这里的坑点:

其实在代码里也总结了,当我们在用SerailizerMethodField()时,可以通过定义方法序列化数据库的关联表数据,加上我们自己的逻辑并得到返回值,但是这样会带来一个问题,在反序列化的时候校验会把这些字段的数据过滤掉,导致create和update出现报错,当然我们可以通过将这些数据添加到validated_data中来达到目的,但这终究不是完美的解决办法,小编才疏学浅,还望有大神路过指点一二

原文地址:https://www.cnblogs.com/dongxixi/p/11130052.html