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