Django(45)drf序列化類的使用(Serializer)

Silent丿丶黑羽發表於2021-06-04

前言

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

Serializer

我們使用序列化類Serializer,我們來看下原始碼結構,這裡推薦使用pycharm左邊導航欄的Structure,可以清晰的看到一個檔案的結構,如下圖
Django(45)drf序列化類的使用(Serializer)
我們會發現Serializer繼承自BaseSerializerSerializerMetaclass,但是Serializer類中又沒有create方法和update方法,所以我們使用的時候必須自己手動定義這2個方法
 

準備工作

1.新建一個專案drf_demo,在專案中新建一個appdrf_app,在app中新建一個檔案urls.py,專案結構如下
Django(45)drf序列化類的使用(Serializer)

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方法,完成校驗通過的資料入庫工作,得到新增的物件

相關文章