前言

上一篇文章我們講述了序列化,這篇就帶大家一起來實現以下序列化

Serializer

我們使用序列化類Serializer,我們來看下原始碼結構,這裡推薦使用pycharm左邊導航欄的Structure,可以清晰的看到一個檔案的結構,如下圖



我們會發現Serializer繼承自BaseSerializerSerializerMetaclass,但是Serializer類中又沒有create方法和update方法,所以我們使用的時候必須自己手動定義這2個方法

準備工作

1.新建一個專案drf_demo,在專案中新建一個appdrf_app,在app中新建一個檔案urls.py,專案結構如下

2.在models.py檔案中寫入如下程式碼

class Student(models.Model):

    SEX_CHOICES = (
(1,'男'),
(2, '女')
) name = models.CharField(max_length=20, verbose_name='姓名')
age = models.IntegerField(null=True, blank=True, verbose_name='年齡')
sex = models.IntegerField(choices=SEX_CHOICES, default=1, verbose_name='性別') class Meta:
db_table = "student"

3.在drf_demo.urls.pydrf_app.urls.py中分別寫入如下程式碼

# drf_demo.urls.py
urlpatterns = [
path('drf/', include('drf_app.urls')),
] # drf_app.urls.py
app_name = "drf_app"
urlpatterns = [
path('student/', views.student),
]

4.在settings.py檔案的MIDDLEWARE中註釋掉django.middleware.csrf.CsrfViewMiddleware,並在INSTALLED_APPS中加入2個app

'rest_framework',
'drf_app'

5.在命令列輸入以下命令,將orm物件對映到資料庫

python manage makemigrations
python manage migrate

6.寫序列化類一般我們都在app專案中新建serializers.py檔案,接下來可以正式編寫序列化類了

序列化類編寫

# Serializer的建構函式的引數:
# 1. instance:需要傳遞一個orm物件,或者是一個queryset物件,用來將orm轉成json
# 2. data:把需要驗證的資料傳遞給data,用來驗證這些資料是不是符合要求
# 3. many:如果instance是一個queryset物件,那麼就需要設定為True,否則為False class StudentSerializer(serializers.Serializer):
# 序列化提供給前臺的欄位個數由後臺決定,可以少提供
# 但是提供的資料庫對應的欄位,名字一定要與資料庫欄位相同 id = serializers.IntegerField(read_only=True)
name = serializers.CharField(required=True, max_length=200)
sex = serializers.IntegerField(required=True)
age = serializers.IntegerField(required=True) def create(self, validated_data):
"""
根據提供的驗證過的資料建立並返回一個新的`Student`例項
"""
return Student.objects.create(**validated_data) def update(self, instance, validated_data):
"""
根據提供的驗證過的資料更新和返回一個已經存在的`Student`例項。
"""
instance.name = validated_data.get('name', instance.name)
instance.age = validated_data.get('name', instance.age)
instance.sex = validated_data.get('name', instance.sex)
instance.save()
return instance # 區域性鉤子 validate_要校驗的欄位名(self, 當前要校驗欄位的值)
def validate_name(self, value):
if 'j' in value.lower():
raise exceptions.ValidationError("名字非法")
return value def validate_sex(self, value):
if not isinstance(value, int):
raise exceptions.ValidationError("只能輸入int型別")
if value != 1 and value != 2 :
raise exceptions.ValidationError("只能輸入男和女")
return value # 全域性鉤子 validate(self, 系統與區域性鉤子校驗通過的所有資料)
def validate(self, attrs):
age = attrs.get('age')
sex = attrs.get('sex')
if age < 22 and sex == 1:
raise exceptions.ValidationError({"age&sex": "男的必須22週歲以上才能結婚"})
return attrs

我們上面程式碼首先定義了序列化的欄位,欄位中的引數都繼承自Field類,引數如下

def __init__(self, read_only=False, write_only=False,
required=None, default=empty, initial=empty, source=None,
label=None, help_text=None, style=None,
error_messages=None, validators=None, allow_null=False):
  • read_only:當為True時表示這個欄位只能讀,只有在返回資料的時候會使用。
  • write_only:當為True時表示這個欄位只能寫,只有在新增資料或者更新資料的時候會用到。比如我們的賬號密碼,只允許使用者提交,後端是不返回密碼給前臺的
  • required:當為True時表示這個欄位必填,不填狀態碼會返回400
  • default:預設值,沒什麼好說的
  • allow_null:當為True時,允許該欄位的值為空

  之後我們又定義了局部鉤子,校驗特殊的欄位,比如需求規定,使用者的性別只能輸入男和女,此時你就可以定義一個鉤子,當然drf自動幫我們做了一些校驗,比如需要的欄位是int型別,你輸入string型別,會自動觸發系統的error,不需要我們額外定義,後面我們會進行測試

  接下來我們又定義了一個全域性的鉤子,意思就是針對整個資料進行校驗,最適合的場景比如密碼重複輸入,一般我們註冊的時候,需要輸入2次密碼,第二次用來確認,這個場景就適合用全域性鉤子

 

編寫完serializers後,我們最後一步,編寫檢視函式,如下:

def student(request):
# 獲取所有的學生
if request.method == "GET":
# 建立一個queryset物件
stu = Student.objects.all()
# 將物件序列化為dict
stu_data = StudentSerializer(instance=stu, many=True).data
return JsonResponse(status=200, data=stu_data, safe=False)
elif request.method == "POST":
data = JSONParser().parse(request)
serializer = StudentSerializer(data=data)
# 校驗欄位是否符合規範
if serializer.is_valid():
# 符合則儲存到資料庫
serializer.save()
return JsonResponse(data=serializer.data, status=200)
return JsonResponse(serializer.errors, status=400)

測試

測試分為GET請求和POST請求

GET請求

我們開啟介面測試工具postman或者apifox,這裡以apifox為例,輸入127.0.0.1:8000/drf/student/,得到了以下結果

[
{
"id": 1,
"name": "jkc",
"sex": 1,
"age": 18
},
{
"id": 2,
"name": "mary",
"sex": 2,
"age": 20
}
]

說明序列化成功,成功地將資料庫的資料通過json的格式返回給了前臺

POST請求

同樣開啟介面工具,輸入127.0.0.1:8000/drf/student/,在body中選擇json格式,輸入如下資料

{
"name": "aaaa",
"sex": 2,
"age": 18
}

執行結果如下:

{
"id": 13,
"name": "aaaa",
"sex": 2,
"age": 18
}

說明我們反序列化也成功了,小夥伴們自己實踐時可以檢視資料庫,會多了一條這樣的資料

接下來我們是否能觸發鉤子函式

測試validate_name鉤子

輸入測試資料

{
"name": "jjj",
"sex": 2,
"age": 18
}

返回結果如下:

{
"name": [
"名字非法"
]
}

測試validate_sex鉤子

輸入測試資料

{
"name": "kkk",
"sex": 3,
"age": 18
}

返回結果如下:

{
"sex": [
"只能輸入男和女"
]
}

測試預設的輸入型別錯誤

輸入測試資料

{
"name": "kkk",
"sex": "???",
"age": 18
}

返回結果如下:

{
"sex": [
"請填寫合法的整數值。"
]
}

測試預設的必填項不填

輸入測試資料

{
"name": "kkk"
}

返回結果如下:

{
"sex": [
"該欄位是必填項。"
],
"age": [
"該欄位是必填項。"
]
}

測試全域性鉤子

輸入測試資料

{
"name": "kkk",
"sex": 1,
"age": 18
}

返回結果如下:

{
"age&sex": [
"男的必須22週歲以上才能結婚"
]
}

總結

  1. 設定必填與選填序列化欄位,設定校驗規則
  2. 為需要額外校驗的欄位提供區域性鉤子函式,如果該欄位不入庫,且不參與全域性鉤子校驗,可以將值取出校驗
  3. 為有聯合關係的欄位們提供全域性鉤子函式,如果某些欄位不入庫,可以將值取出校驗
  4. 重寫create方法,完成校驗通過的資料入庫工作,得到新增的物件