在使用DRF進行反序列過程中,總是需要校驗欄位,然後返回錯誤結果。可以使用預設的自定義校驗項,也可以自定義校驗項。而預設的自定義校驗項總是差強人意。
版本
Django 2.2.3
Python 3.8.8
djangorestframework 3.13.1
目標效果
預設錯誤返回效果 —— 預設校驗函式
預設錯誤返回 —— 預設校驗函式
def validate(self, attrs):
"""
驗證欄位
"""
# 確認手機號是否唯一
user = User.objects.get(mobile=attrs.get('mobile'))
print(user)
if user:
raise serializers.ValidationError(detail={'status': 1, 'message': '當前手機號碼已經被註冊!', 'data': None})
預設錯誤返回效果 —— 自定義校驗函式
採用自定義校驗函式之後,返回格式有所改善,但是此種返回都是以欄位名為key,自定義的內容格式成為了這個key的value。
預設錯誤返回 —— 自定義校驗函式
def validate_mobile(self, mobile):
"""
驗證手機號
"""
try:
# 確認手機號是否唯一
user = User.objects.get(mobile=mobile)
print(user)
if user:
raise serializers.ValidationError(detail={'status': 1, 'message': '當前手機號碼已經被註冊!', 'data': None})
except User.DoesNotExist:
pass
return mobile
自定義校驗函式說明
- 方法名必須以validate_作為字首,字尾為對應的欄位名:如
validate_mobile
中的mobile
即為模型的一個欄位 - 一定要返回校驗之後的值:
return mobile
- 不需要放在validators的列表中就可以生效(暫未理解為何意)
避坑: 方法名字首是validate_
為字首,而不是validated_
為字首。Pycharm提示時會出現 validated_xxx
,此時多了一個d
自定義ValidationError
返回
# -*- coding:utf-8 -*-
# author: F0080
# Python 3.8.8
# FilePath: 專案/utils/validation_error.py,如demo/utils/validation_error.py
# FilePath可隨意定義,但注意後面引入路徑
from django.utils.translation import ugettext_lazy as u_
from rest_framework import status
from rest_framework.exceptions import APIException, ErrorDetail
from rest_framework.utils.serializer_helpers import ReturnList, ReturnDict
def get_error_details(data, default_code=None):
if isinstance(data, list):
ret = [
get_error_details(item, default_code) for item in data
]
if isinstance(data, ReturnList):
return ReturnList(ret, serializer=data.serializer)
return ret
elif isinstance(data, dict):
ret = {
key: get_error_details(value, default_code) for key, value in data.items()
}
print(ret)
if isinstance(data, ReturnDict):
return ReturnDict(ret, serializer=data.serializer)
return ret
code = getattr(data, 'code', default_code)
# 返回一個物件屬性值
return ErrorDetail(data, code)
class ValidationError400(APIException):
status_code = status.HTTP_400_BAD_REQUEST
default_detail = u_('Invalid input.')
default_code = 'invalid'
def __init__(self, detail=None, code=None):
if detail is None:
detail = self.default_detail
if code is None:
code = self.default_code
if not isinstance(detail, dict) and not isinstance(detail, list):
detail = [detail]
self.detail = get_error_details(detail, code)
在serializers.py中使用自定義ValidationError
from demo.utils.validation_error.py import ValidationError400
...
def validate_mobile(self, mobile):
"""
自定義校驗函式——驗證手機號
"""
try:
# 確認手機號是否唯一
user = User.objects.get(mobile=mobile)
print(user)
if user:
raise ValidationError400(detail={'status': 1, 'message': '當前手機號碼已經被註冊!', 'data': None})
except User.DoesNotExist:
pass
return mobile
def validate(self, attrs):
"""
預設校驗函式——驗證密碼
"""
password = attrs.get('password')
# 驗證密碼長度6-16位
if not re.match('^.{6,16}$', password):
raise ValidationError400(detail={'status': 1, 'message': '密碼長度必須在6-16位之間!', 'data': None})
...
致謝大佬
rest_framework serializers ValidationError 錯誤資訊自定義 key 值
DjangoRestFramework ModelSerializer:欄位級驗證不起作用