drf序列化和反序列化
一、自定義序列化
# models.py
from django.db import models
class User(models.Model):
CHOICE_SEX = ((0, '男'), (1, '女'))
name = models.CharField(max_length=100)
height = models.DecimalField(max_digits=6, decimal_places=2, default=1.85)
age = models.IntegerField(default=0)
icon = models.ImageField(upload_to='icon', default='default.jpg')
sex = models.IntegerField(choices=CHOICE_SEX)
class Meta:
db_table = 'db_user'
verbose_name = '用戶表'
verbose_name_plural = verbose_name
def __str__(self):
return self.name
# urls.py
urlpatterns = [
url(r'^books/$', views.MyAPIView.as_view()),
url(r'^books/(?P<pk>\d+)/', views.MyAPIView.as_view()),
# 在路由配置向外面開放的接口 media
url(r'^media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT})
]
from api import models
from django.conf import settings
from rest_framework.response import Response
from rest_framework.views import APIView
class MyAPIView(APIView):
def get(self, *args, **kwargs):
pk = kwargs.get("pk", None)
if pk:
print(pk)
user_obj = models.User.objects.filter(pk=pk).first()
return Response({
"status": 0,
'msg': "get ok",
'result': {
"name": user_obj.name,
'age': user_obj.age,
"icon": f'http://127.0.0.1:8000{settings.MEDIA_URL}{user_obj.icon}',
"sex": user_obj.get_sex_display()
}
})
else:
user_list = []
user_list_obj = models.User.objects.all()
for user_obj in user_list_obj:
user_list.append(
{
'result': {
"name": user_obj.name,
'age': user_obj.age,
"icon": f'http://127.0.0.1:8000{settings.MEDIA_URL}{user_obj.icon}',
"sex": user_obj.get_sex_display()
}
}
)
print(user_list)
return Response({
"status": 0,
'msg': "get ok",
'result': user_list
})
def post(self, *args, **kwargs):
return Response({
"status": 1
})
總結:
- 通過ORM操作獲取數據庫拿到資源數據
- 將獲取的的數據進行序列化,序列化之后將數據返回給前台的數據
- 通過Response返回格式化后的數據
1.1 設置國際化
# 在settings中設置了國際化才可以顯示中文
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False
二、通過視圖類的序列化和反序列化
# 在settings中設置了國際化才可以顯示中文
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False
2.1通過視圖類序列化
自定義序列化類
# 創建自定義.py並創建序列化類
class MySerialize(serializers.Serializer):
name = serializers.CharField()
age = serializers.IntegerField()
# icon = serializers.ImageField()
# sex = serializers.IntegerField()
# 小數字段,必須要設置參數
height = serializers.DecimalField(max_digits=6, decimal_places=3)
# 自定義字段
gender = serializers.SerializerMethodField()
icon = serializers.SerializerMethodField()
# 自定義序列化字段,序列化的屬性值由方法來提供,
# 方法的名字:固定為 get_屬性名,
# 方法的參數:序列化對象,序列化的model對象
# 強烈建議自定義序列化字段名不要與model已有的屬性名重名
def get_gender(self, obj):
# print(obj, type(obj)) # obj類<class 'api.models.User'>
# print(self, type(self)) # class 'api.myserialize.MySerialize'>
return obj.get_sex_display()
def get_icon(self, obj):
return f'http://127.0.0.1:8000/media/{obj.icon}'
視圖類
# 通過serialize實現序列化
from .myserialize import MySerialize
class MyAPIView(APIView):
def get(self, request, *args, **kwargs):
pk = kwargs.get("pk", None)
if pk:
user_obj = models.User.objects.filter(pk=pk).first()
# 自定義單序列化
my_serialize = MySerialize(user_obj)
print(my_serialize.data)
# if my_serialize.is_valid():
# res = my_serialize
return Response({
"status": 0,
'msg': "get ok",
'result': my_serialize.data
})
else:
user_list_obj = models.User.objects.all()
# 群序列化
my_serialize = MySerialize(user_list_obj, many=True)
print(my_serialize.data, type(my_serialize.data))
return Response({
"status": 0,
'msg': "get ok",
'result': my_serialize.data
})
總結:
- 直接將要序列化的數據傳給序列化類
- 要序列化的數據如果是單個對象,序列化的參數many為False,數據如果是多個對象(list,queryset)序列化的參數many為True
- 序列化類的中字段必須與model中字段屬性同名,如果不參與序列化的model屬性,在序列化類中不做聲明,也不會返回
- 可以自定義序列化字段,自定義序列化字段用 SerializerMethodField() 作為字段類型,序列化的屬性有方法類提供,方法的名字:get_屬性名,方法參數:序列化對象,序列化的model對象,注意自定義的序列化字段不要與model已有字段屬性重名,返回方法的返回值
2.2通過視圖類反序列化
class UserCreateSerialize(serializers.Serializer):
# 系統校驗規則
# 系統必須反序列化的字段,序列化的字段,序列化屬性名不是必須有與model屬性名字對應
# 但是與之對應會方便序列化將校驗通過的數據與數據庫進行交互,
name = serializers.CharField(min_length=3, max_length=6, required=False,error_messages={
'required': "姓名必填",
"min_length": "太短"
})
# # 系統可選的反序列化字段:沒有提供不進行校驗(數據庫中有默認值或可以為空),提供了就進行校驗
age = serializers.IntegerField(min_value=3, max_value=6, required=False)
# icon = serializers.ImageField()
sex = serializers.IntegerField()
# 小數字段,必須要設置參數
height = serializers.DecimalField(max_digits=6, decimal_places=3)
pwd = serializers.CharField(min_length=3, max_length=64)
# 自定義反序列化字段:一定參與校驗,且要在校驗過程中,將其從入庫的數據中取出,剩余與model對應的數據才會入庫
re_pwd = serializers.CharField(min_length=3, max_length=64)
par = serializers.CharField(max_length=10)
# 自定義校驗規則:局部鈎子,全局鈎子
# 局部鈎子: validate_字段名(slef, 字段值)
# 規則: 成功返回value,校驗錯誤拋異常
def validate_par(self, value):
if 'rand' in value.lower():
raise serializers.ValidationError("名字不能有randy")
return value
# 全局鈎子:validate(self, 所有校驗的數據字典)
# 規則:成功返回attrs,失敗拋異常
def validate(self, attrs):
pwd = attrs.get("pwd")
re_pwd = attrs.pop('re_pwd')
if pwd != re_pwd:
raise serializers.ValidationError({"re_pwd": "兩次密碼不能一樣!"})
return attrs
# create重寫,
def create(self, validated_data):
return models.User.objects.create(**validated_data)
class MyAPIView(APIView):
# 單增和群增
def post(self, request, *args, **kwargs):
# 從請求對象中拿到前台的數據
# 校驗數據是否合法
# 反序列化成后台Model對象與數據交互
request_data = request.data
user_seria = UserCreateSerialize(data=request_data)
# 調用反序列化的校驗規則,系統規則,自定義規則(局部鈎子,全局鈎子)
result = user_seria.is_valid()
if result:
# 校驗通過,可以與數據庫進行交互增 create,該(update)
user_obj = user_seria.save()
return Response({
'status': 0,
'msg': 'ok',
'results': MySerialize(user_obj).data
})
else:
# 校驗失敗,返回錯誤信息,在使用 .errors必須先要調用 is_valid()方法
return Response({
"status": 1,
"msg": user_seria.errors,
}, status=status.HTTP_400_BAD_REQUEST
)
總結:
- 在視圖類中,從請求對象中獲取前台的數據,校驗前台數據是否合法,然后將合法的數據返回給Model對象與數據進行交互,保存數據
- 反序列化的數據如果是單個數據,反序列化的參數mangy為False,數據如果是多個字典的列表,反序列化的參數many為True
- user_seria.errors,在報錯的時候一定要先使用 is_valid()函數
- 反序列化類:系統的字段可以在Filed類型中設置系統校驗的規則(name=serializers.CharField(min_length=3))
- required校驗規則對該字段是必校驗還是可選校驗字段(默認required為True,數據庫字段有默認值或可以為空的字段可以將required賦值為False)
- 自定義的反序列字段,設置系統校驗規則同系統字段,但是需要在自定義校驗規則中(局部、全局鈎子)將自定義反序列化字段取出(返回剩余的數據與數據庫交互)
- 局部鈎子的方法命名 validate_屬性名(self, 屬性的value),校驗規則為 成功返回屬性的value 失敗拋出校驗錯誤的異常
- 全局鈎子的方法命名 validate(self, 所有屬性attrs),校驗規則為 成功返回attrs 失敗拋出校驗錯誤的異常
- 校驗有個順序先校驗全部聲明的字段,只有全部校驗成功之后才會去校驗局部鈎子,然后在校驗全局鈎子;,
- 反序列化需要重寫create,update方法
三、ModelSerializer類實現序列化和反序列化
3.1 序列化
from rest_framework.serializers import ModelSerializer
# 自定反序列化類
class UserModelSerializer(ModelSerializer):
# source指定數據的來源,"product.product_id"是models中表的字段
product_id = serializers.IntegerField(source="product.product_id")
image_url = serializers.ImageField(source="image.image_url")
class Meta:
model = models.User
fields = ["name", "age", "height", "sex", 'gender'] # 序列化字段
# 自定義插拔序列化字段(在model類中實現):
#替換了在Serializer類中自定義的序列化字段(SerializerMethodField)
# 自定義插拔序列化字段一定不參與反序列化過程
@property
def gender(self):
return self.get_sex_display()
from .myserialize import UserModelSerializer
# 視圖類
class MyAPIView(APIView):
# 單查和群查
def get(self, request, *args, **kwargs):
pk = kwargs.get("pk", None)
if pk:
user_obj = models.User.objects.filter(pk=pk).first()
if not user_obj:
return Response(data={"status": 1, 'msg': "數據不存在"})
# 序列化數據context 將request傳遞過去則會自動憑借url的資源,比如圖片
user_data = UserModelSerializer(user_obj, many=False,context={"request": request}).data
return Response({
'status': 0,
'msg': '單查 ok',
'results': user_data
})
# 群查
user_query = models.User.objects.all()
# 獲取序列化
user_list_data = UserModelSerializer(user_query, many=True).data
return Response({
"status": 0,
"msg": "群查 ok",
"results": user_list_data
})
3.2 反序列化
from rest_framework.serializers import ModelSerializer
# 自定反序列化類
from rest_framework.serializers import ModelSerializer
class UserModelSerializer(ModelSerializer):
# 將序列化類與Model類進行綁定
# 設置序列化與反序列化所有字段(並划分序列化字段與反序列化字段)
# 設置反序列化的局部與全局鈎子
# 自定義反序列化字段,校驗規則只能生命自定義反序列化字段是設置,且一定要寫write_only
re_pwd = serializers.CharField(min_length=3, max_length= 5, write_only=True)
class Meta:
model = models.User
fields = ["name", "age", "height", "sex", 'gender', "pwd", "re_pwd"]
extra_kwargs = {
"name": {
'required': True,
"min_length": 3,
"error_messages": {
"min_length": "太短了"
}
},
"age": {
# 數據庫中有默認值或可以為空字段, required默認為false,前台發送的數據不會進行校驗,只有為true時候才會進行校驗
"required": True,
'min_value': 5,
"read_only": True, # 只參與序列化,不參與反序列化進行校驗
},
"pwd": {
"required": True,
"write_only": True, # 只參與反序列化 設置這個就是在反序列化的時候不會返回給前台,不會序列化
},
"height": {
# 只參與序列化,不參與反序列化進行校驗,就是校驗數據傳來了height這個參數,不會對數據進行校驗只會返回原先的數據或者是默認的數據
"read_only": True,
}
}
# 局部鈎子
def validate_name(self, value):
if 'randy' in value.lower():
raise serializers.ValidationError('名字中不能有randy')
return value
# 全局鈎子
def validate(self, attrs):
pwd = attrs.get('pwd')
# 一定要移除
re_pwd = attrs.pop('re_pwd')
if pwd != re_pwd:
raise serializers.ValidationError({'re_pwd': '兩次密碼不一致'})
return attrs
from .myserialize import UserModelSerializer
# 視圖類
class MyAPIView(APIView):
# 單增和群增
def post(self, request, *args, **kwargs):
# 獲取校驗數據
request_data = request.data
user_seria = UserModelSerializer(data=request_data)
# 校驗失敗直接拋異常,反饋異常信息給前台,只要校驗通過代碼才會往下執行
# result = user_seria.is_valid(raise_exception=True)
result = user_seria.is_valid(raise_exception=True)
if result:
# 校驗通過,可以與數據庫進行交互增 create,該(update)
user_obj = user_seria.save()
return Response({
'status': 0,
'msg': 'ok',
'results': UserModelSerializer(user_obj).data
})
else:
# 校驗失敗,返回錯誤信息,在使用 .errors必須先要調用 is_valid()方法
return Response({
"status": 1,
"msg": user_seria.errors,
}, status=status.HTTP_400_BAD_REQUEST
)
總結:
-
將序列化與反序列化功能整合到一個類,這個類繼承
ModelSerializer
來實現 -
繼承
ModelSerializer
類的資源序列化類內部包含三部分, Meta子類, 局部鈎子、全局鈎子- 注:create和update方法ModelSerializer已經重寫了,使用不需要重寫,默認不能實現群該功能,
- 通過集成ListSerializer類update方法,實現群該功能,將其綁定給list_serializer_class
-
在Meta子類中:
- 用model來綁定所關聯的Model類
- 用fields來設置所有的序列化反序列化字段
- 用extra_kwargs來設置系統的校驗規則
-
重要字段校驗規則
- read_only校驗規則:代表該字段只參與序列化,不參與反序列化進行校驗,就是校驗數據傳來了height這個參數,不會對數據進行校驗只會返回原先的數據或者是默認的數據
- write_only校驗規則:只參與反序列化 設置這個就是在反序列化的時候不會返回給前台,不會序列化
- required校驗規則:代表該字段在反序列化,在數據庫中有默認值或可以為空字段, required默認為false,前台發送的數據不會進行校驗,只有為true時候才會進行校驗
- 如果一個字段有默認值或是可以為空,沒設置required規則,默認為False,反之默認值為True
- 如果一個Model字段即沒有設置read_only也沒設置write_only,該字段默認參與序列化及反序列化
-
自定義序列化字段:在Model類中,定義方法屬性(可以返回特殊值,還可以完成連表操作),在序列化類的fields屬性中可以選擇性插拔(重要)自定義插拔序列化字段一定不參與反序列化過程
-
自定義反序列化字段:在Serializer類中,自定義校驗字段,校驗規則也只能在聲明字段時設置,自定義的反序列化字段(如re_pwd),必須設置write_only為True 自定義插拔序列化字段一定不參與反序列化過程
-
user_seria = UserModelSerializer(data=request_data),校驗數據要將數據給data,才可以校驗
-
在全局鈎子中 一定要移除重復確認的字段
-
在反序列化的時候如果需要字段一定要傳則在model中不給他設置默認值,一旦設置了默認值,反序列化的時候就可以不傳值
-
使用fields來明確字段,
__all__
表名包含所有字段,也可以寫明具體哪些字段, -
使用exclude可以明確排除掉哪些字段
-
可以通過read_only_fields指明只讀字段,即僅用於序列化輸出的字段
-
可以使用extra_kwargs參數為ModelSerializer添加或修改原有的選項參數