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__'  # 返回全部的字段


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM