• 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即可执行更新操作

  • 相关阅读:
    【CSP模拟赛】益智游戏(最短路(DJSPFA)&拓扑排序)
    【CSP模拟赛】仔细的检查(树的重心&树hash)
    【CSP模拟赛】奇怪的队列(树状数组 &二分&贪心)
    【洛谷】P1275 魔板(暴力&思维)
    【CSP模拟赛】方程(数学)
    【洛谷】P3177 [HAOI2015]树上染色
    【洛谷】P3188 [HNOI2007]梦幻岛宝珠
    方法重载(一)
    找最大值
    java 冒泡排序
  • 原文地址:https://www.cnblogs.com/gwklan/p/11140154.html
Copyright © 2020-2023  润新知