zoukankan      html  css  js  c++  java
  • python测试开发django-rest-framework-88.反序列化(ModelSerializer)之校验传入参数

    前言

    serializers.Serializer可以对modle模型中的字段序列化,并且必须写create和update两个方法。ModelSerializer可以看成是Serializer的一个升级版,功能更强大,更方便。
    实际上ModelSerializer类继承了Serializer类

    序列化

    序列化是把数据库里面的数据,转成json格式返回给用户,具体参考前面这篇https://www.cnblogs.com/yoyoketang/p/11538172.html
    在models.py设计一个Goods商品表,里面包含多个字段和多个数据类型

    from django.db import models
    # Create your models here.
    # 作者-上海悠悠 QQ交流群:717225969
    # blog地址 https://www.cnblogs.com/yoyoketang/
    
    
    
    class Goods(models.Model):
        """商品表"""
        goods_name = models.CharField(max_length=30,
                                      default="",
                                      verbose_name="商品名称")
        goods_code = models.CharField(max_length=30,
                                      unique=True,
                                      verbose_name="商品代号")
        merchant_id = models.CharField(max_length=30,
                                       default="",
                                       blank=True, null=True,
                                       verbose_name="商户ID")
        merchant_name = models.CharField(max_length=30,
                                         default="",
                                         blank=True, null=True,
                                         verbose_name="商户名称")
        goods_price = models.FloatField(blank=True, null=True,
                                        default=0,
                                        verbose_name="商品价格")
        goods_stock = models.IntegerField(blank=True, null=True,
                                          default=0,
                                          verbose_name="商品库存")
        goods_groupid = models.IntegerField(blank=True, null=True,
                                            default=0,
                                            verbose_name="商品分组")
        goods_status = models.IntegerField(choices=(
                                                    (0, '下架'), 
                                                    (1, '出售中')
                                                   ),
                                           default=1,
                                           verbose_name="0下架 1出售中")
    
        price = models.FloatField(blank=True, null=True,
                                  default=0,
                                  verbose_name="成本价")
    
        create_time = models.DateTimeField(auto_now_add=True, verbose_name="添加时间")
        update_time = models.DateTimeField(auto_now=True, verbose_name="修改时间")
    
        class Meta:
            verbose_name_plural = '商品'
            verbose_name = "商品信息"
    
        def __str__(self):
            return self.goods_code
    

    view视图

    视图继承 drf 的 APIView,这里写了2个方法,get 查询全部商品,序列化后返回json数据。
    post 请求是创建商品。

    from rest_framework.views import APIView
    from rest_framework import serializers
    from rest_framework.response import Response
    from rest_framework.permissions import AllowAny, IsAuthenticated, IsAdminUser
    from rest_framework.authentication import TokenAuthentication
    from .models import Goods
    
    # Create your views here.
    # 作者-上海悠悠 QQ交流群:717225969
    # blog地址 https://www.cnblogs.com/yoyoketang/
    
    
    class GoodsSerializer(serializers.ModelSerializer):
        """序列化商品models"""
        create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S',required=False)
        update_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S',required=False)
    
        class Meta:
            model = Goods
            fields = '__all__'  # 返回全部的字段
            # exclude是不包含某些字段
            # exclude = ["price"]
    
    
    class GoodsAPIView(APIView):
        """商品视图"""
        permission_classes = (AllowAny,)      # AllowAny 允许所有用户
    
        def get(self, request, *args, **kwargs):
            """返回所有的"""
            goods = Goods.objects.all()   # 查询全部
            serializer = GoodsSerializer(instance=goods, many=True)
    
            return Response({
                "code": 0,
                "msg": "success!",
                "data": serializer.data
            })
    
    
        def post(self, request, *args, **kwargs):
            """提交数据"""
            verify_data = GoodsSerializer(data=request.data)
            if verify_data.is_valid():
                save = verify_data.save()
                return Response({
                    "code": 0,
                    "msg": "success!",
                    "data": GoodsSerializer(instance=save).data
                })
            else:
                return Response({
                    "code": 10086,
                    "msg": "参数不合法",
                    "data": verify_data.errors
                })
    
    

    urls.py配置访问路由

    from django.conf.urls import url
    from yoyo import views
    
    urlpatterns = [
        url('^api/v1/goods/$', views.GoodsAPIView.as_view()),
    ]
    

    序列化和反序列化

    什么是序列化呢?

    当用户需要查询数据的时候,把数据库里面的数据转成我们需要的json数据,这个过程就是序列化

    在get方法里实例化GoodsSerializer对象,传2个参数

    • instance 是查询的queryset对象,也可以是单个Goods 的object对象
    • many 如果是queryset对象,就需要带上many=True ,说明是一个list,有多个数据,如果是单个object对象,就不需要这个参数
        def get(self, request, *args, **kwargs):
            """返回所有的"""
            goods = Goods.objects.all()   # 查询全部
            serializer = GoodsSerializer(instance=goods, many=True)
    

    上面这个过程就是序列化,序列化后输出数据serializer.data

    什么是反序列化?

    用户在添加商品的时候,需要把数据存到数据库,这个过程我们需要先校验是不是合法的。

    对用户传入的数据,我们需要先清洗下,因为用户可能会传一些数据库表里面没有的字段,这些我们不需要,于是可以用到 GoodsSerializer(data=request.data)

    • data 用户传入的参数,通过request.data 获取,request.data 实际上跟之前request.POST 是一样的获取用户传过来的数据
    • is_valid() 校验数据是否合法
    • save() 保存之前,必须先调用is_valid(),保存后返回一个Goods object对象
        def post(self, request, *args, **kwargs):
            """提交数据"""
            verify_data = GoodsSerializer(data=request.data)
    

    上面这个过程,用户传过来的数据先清洗,校验数据合法性,再存入数据库的过程,就是反序列化

    校验用户数据必传项required=True

    看过接口文档的应该知道,有些参数是必传的,有些是非必传的,那么我们可以在GoodsSerializer控制字段的必传和非必传
    详情参考前面这篇https://www.cnblogs.com/yoyoketang/p/14291206.html

    class GoodsSerializer(serializers.ModelSerializer):
        """序列化商品models"""
        create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S',required=False)
        update_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S',required=False)
    
        # 必传字段
        goods_code = serializers.CharField(required=True)
        goods_stock = serializers.IntegerField(required=True)
    
        class Meta:
            model = Goods
            fields = '__all__'  # 返回全部的字段
    

    序列化的时候,设置 goods_code 和 goods_stock 是必传字段,那么在添加商品的时候,如果不传就会提示

    {"goods_code":["该字段是必填项。"],"goods_stock":["该字段是必填项。"]}}
    

    校验忽略某些字段read_only=True

    如果在创建商品的时候,有些字段我不想让用户去修改,比如 goods_status(商品状态),默认就是出售中,

    不想让用户创建的时候设置下架,于是可以忽略 goods_status(商品状态) 字段, 设置 read_only=True

    # 作者-上海悠悠 QQ交流群:717225969
    # blog地址 https://www.cnblogs.com/yoyoketang/
    
    
    class GoodsSerializer(serializers.ModelSerializer):
        """序列化商品models"""
        create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S',required=False)
        update_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S',required=False)
    
        # 必传字段
        goods_code = serializers.CharField(required=True)
        goods_stock = serializers.IntegerField(required=True)
    
        # 忽略字段,设置read_only=True
        goods_status = serializers.IntegerField(read_only=True)
    
        class Meta:
            model = Goods
            fields = '__all__'  # 返回全部的字段
    

    现在不管 goods_status 传什么都不会影响保存结果

    校验字符串和整数范围

    校验 goods_code 字符串长度是8-15位,校验goods_stock 整数范围是1-10000

    class GoodsSerializer(serializers.ModelSerializer):
        """序列化商品models"""
        create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S',required=False)
        update_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S',required=False)
    
        # 必传字段
        goods_code = serializers.CharField(required=True,
                                           max_length=15,
                                           min_length=8)
        goods_stock = serializers.IntegerField(required=True,
                                               min_value=1,
                                               max_value=10000)
    
        # 忽略字段,设置read_only=True
        goods_status = serializers.IntegerField(read_only=True)
    
    
    
        class Meta:
            model = Goods
            fields = '__all__'  # 返回全部的字段
    

    这时候就会对传入的字符串和整数范围做校验

    自定义校验字段

    如果我想用户的商品code命名,必须按sp开头,针对某个字段单独写校验方式,可以自定义 validate_<field_name>

    • value 参数是传入的数据
    • raise 抛出的异常会serializers.ValidationError("goods_code 不是 sp 开头"), 会在 verify_data.errors显示
    # 作者-上海悠悠 QQ交流群:717225969
    # blog地址 https://www.cnblogs.com/yoyoketang/
    
    
    class GoodsSerializer(serializers.ModelSerializer):
        """序列化商品models"""
        create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S',required=False)
        update_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S',required=False)
    
        # 必传字段
        goods_code = serializers.CharField(required=True,
                                           max_length=15,
                                           min_length=8)
        goods_stock = serializers.IntegerField(required=True,
                                               min_value=1,
                                               max_value=10000)
    
        # 忽略字段,设置read_only=True
        goods_status = serializers.IntegerField(read_only=True)
    
        def validate_goods_code(self, value):
            """校验字段 validate_<Field>"""
            if not value.startswith("sp"):
                raise serializers.ValidationError("goods_code 不是 sp 开头")
            return value
    
        class Meta:
            model = Goods
            fields = '__all__'  # 返回全部的字段
    
    

    多个字段校验

    如果我想校验 goods_price(商品售卖价格)不能小于 (price)成本价, 万一哪个运营设置商品价低于成本价,那不得亏惨!
    这里涉及到传入参数的2个值互相校验

    # 作者-上海悠悠 QQ交流群:717225969
    # blog地址 https://www.cnblogs.com/yoyoketang/
    
    
    class GoodsSerializer(serializers.ModelSerializer):
        """序列化商品models"""
        create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S',required=False)
        update_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S',required=False)
    
        # 必传字段
        goods_code = serializers.CharField(required=True,
                                           max_length=15,
                                           min_length=8)
        goods_stock = serializers.IntegerField(required=True,
                                               min_value=1,
                                               max_value=10000)
    
        # 忽略字段,设置read_only=True
        goods_status = serializers.IntegerField(read_only=True)
    
        def validate_goods_code(self, value):
            """校验字段 validate_<Field>"""
            if not value.startswith("sp"):
                raise serializers.ValidationError("goods_code 不是 sp 开头")
            return value
    
        def validate(self, attrs):
            """自定义校验"""
            goods_price = attrs.get('goods_price', 0)
            price = attrs.get('price', 0)
            if goods_price < price:
                raise serializers.ValidationError('goods_price 不能小于 price')
            return attrs
    
        class Meta:
            model = Goods
            fields = '__all__'  # 返回全部的字段
    

  • 相关阅读:
    [SCOI2009] Windy数
    [P1361] 小M的作物
    Wannafly Camp 2020 Day 2E 阔力梯的树
    2017百越杯反序列化writeup
    大美西安writeup
    Thinkphp的SQL查询方式
    Thinkphp的CURD
    记一次拿webshell踩过的坑(如何用PHP编写一个不包含数字和字母的后门)
    ThinkPHP的输出和模型使用
    ThinkPHP的运行流程-2
  • 原文地址:https://www.cnblogs.com/yoyoketang/p/14342631.html
Copyright © 2011-2022 走看看