1. 程式人生 > >Django-Rest-Framework的序列化之serializers 序列化元件

Django-Rest-Framework的序列化之serializers 序列化元件

Django-Rest-Framework的序列化之serializers 序列化元件

 

 

正常的序列化

from django.http import HttpResponse, JsonResponse
from django.views import View
from demo import models
import json
from django.core import serializers

class BookView(View):
def get(self, request):
    book_queryset = models.Book.objects.all().values("id", 'title')
    book_list = list(book_queryset)
    # 方式一
    # ret = json.dumps(book_list, ensure_ascii=False)
    # return HttpResponse(ret)
    # 方式二 Django的序列化
    # book_list_obj = models.Book.objects.all()
    # ret = serializers.serialize('json', book_list_obj, ensure_ascii=False)
    # return HttpResponse(ret)
    # 方式三
    return JsonResponse(book_list, safe=False, json_dumps_params={"ensure_ascii": False})

 

為什麼要用序列化元件

當我們做前後端分離的專案~~我們前後端互動一般都選擇JSON資料格式,JSON是一個輕量級的資料互動格式。

那麼我們給前端資料的時候都要轉成json格式,那就需要對我們從資料庫拿到的資料進行序列化。

接下來我們看下django序列化和rest_framework序列化的對比~~

 

Django的序列化方法

.values序列化結果

class BooksView(View):
    def get(self, request):
        book_list = Book.objects.values("id", "title", "chapter", "pub_time", "publisher")
        book_list = list(book_list)
        # 如果我們需要取外來鍵關聯的欄位資訊 需要迴圈獲取外來鍵 再去資料庫查然後拼接成我們想要的
        ret = []
        for book in book_list:
            pub_dict = {}
            pub_obj = Publish.objects.filter(pk=book["publisher"]).first()
            pub_dict["id"] = pub_obj.pk
            pub_dict["title"] = pub_obj.title
            book["publisher"] = pub_dict
            ret.append(book)
        ret = json.dumps(book_list, ensure_ascii=False, cls=MyJson)
        return HttpResponse(ret)


# json.JSONEncoder.default()
# 解決json不能序列化時間欄位的問題
class MyJson(json.JSONEncoder):
    def default(self, field):
        if isinstance(field, datetime.datetime):
            return field.strftime('%Y-%m-%d %H:%M:%S')
        elif isinstance(field, datetime.date):
            return field.strftime('%Y-%m-%d')
        else:
            return json.JSONEncoder.default(self, field)

django serializers

from django.core import serializers

# 能夠得到我們要的效果 結構有點複雜
class BooksView(View):
    def get(self, request):
        book_list = Book.objects.all()
        ret = serializers.serialize("json", book_list)
        return HttpResponse(ret)

 

DRF序列化的方法

首先,我們要用DRF的序列化,就要遵循人家框架的一些標準,

-- Django我們CBV繼承類是View,現在DRF我們要用APIView

-- Django中返回的時候我們用HTTPResponse,JsonResponse,render ,DRF我們用Response

 

序列化

 

第一步 宣告序列化類

class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    title = serializers.CharField(max_length=32)
    CHOICES = ((1, "Linux"), (2, "Django"), (3, "Python"))
    chapter = serializers.ChoiceField(choices=CHOICES, source="get_chapter_display")
    pub_time = serializers.DateField()

 

第二步 序列化物件

from rest_framework.views import APIView
from rest_framework.response import Response

class BookView(APIView):
    def get(self, request):
        book_list = Book.objects.all()
        ret = BookSerializer(book_list, many=True)
        return Response(ret.data)

 

外來鍵關係的序列化

from rest_framework import serializers
from .models import Book


class PublisherSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(max_length=32)


class UserSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    name = serializers.CharField(max_length=32)
    age = serializers.IntegerField()


class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(max_length=32)
    CHOICES = ((1, "Linux"), (2, "Django"), (3, "Python"))
    chapter = serializers.ChoiceField(choices=CHOICES, source="get_chapter_display", read_only=True)
    pub_time = serializers.DateField()

    publisher = PublisherSerializer(read_only=True)
    user = UserSerializer(many=True, read_only=True)

 

反序列化

當前端給我們發post的請求的時候~前端給我們傳過來的資料~我們要進行一些校驗然後儲存到資料庫~

這些校驗以及儲存工作,DRF的Serializer也給我們提供了一些方法了~~

首先~我們要寫反序列化用的一些欄位~有些欄位要跟序列化區分開~~

Serializer提供了.is_valid()  和.save()方法~~

 

.save()

反序列化 serializer.py

class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(max_length=32)
    CHOICES = ((1, "Linux"), (2, "Django"), (3, "Python"))
    chapter = serializers.ChoiceField(choices=CHOICES, source="get_chapter_display", read_only=True)
    w_chapter = serializers.IntegerField(write_only=True)
    pub_time = serializers.DateField()

    publisher = PublisherSerializer(read_only=True)
    user = UserSerializer(many=True, read_only=True)

    users = serializers.ListField(write_only=True)
    publisher_id = serializers.IntegerField(write_only=True)

    def create(self, validated_data):
        book = Book.objects.create(title=validated_data["title"], chapter=validated_data["w_chapter"], pub_time=validated_data["pub_time"],                                  publisher_id=validated_data["publisher_id"])
        book.user.add(*validated_data["users"])
        return book

序列化 views.py

class BookView(APIView):
    def get(self, request):
        book_list = Book.objects.all()
        ret = BookSerializer(book_list, many=True)
        return Response(ret.data)

    def post(self, request):
        # book_obj = request.data
        print(request.data)
        serializer = BookSerializer(data=request.data)
        if serializer.is_valid():
            print(12341253)
            serializer.save()
            return Response(serializer.validated_data)
        else:
            return Response(serializer.errors)

當前端給我們傳送patch請求的時候,前端傳給我們使用者要更新的資料,我們要對資料進行部分驗證~~

 

.is_valid()

PATCH請求serializers.py

class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(max_length=32)
    CHOICES = ((1, "Linux"), (2, "Django"), (3, "Python"))
    chapter = serializers.ChoiceField(choices=CHOICES, source="get_chapter_display", read_only=True)
    w_chapter = serializers.IntegerField(write_only=True)
    pub_time = serializers.DateField()

    publisher = PublisherSerializer(read_only=True)
    user = UserSerializer(many=True, read_only=True)

    users = serializers.ListField(write_only=True)
    publisher_id = serializers.IntegerField(write_only=True)

    def create(self, validated_data):
        book = Book.objects.create(title=validated_data["title"], chapter=validated_data["w_chapter"], pub_time=validated_data["pub_time"],
                                publisher_id=validated_data["publisher_id"])
        book.user.add(*validated_data["users"])
        return book

    def update(self, instance, validated_data):
        instance.title = validated_data.get("title", instance.title)
        instance.chapter = validated_data.get("w_chapter", instance.chapter)
        instance.pub_time = validated_data.get("pub_time", instance.pub_time)
        instance.publisher_id = validated_data.get("publisher_id", instance.publisher_id)
        if validated_data.get("users"):
            instance.user.set(validated_data.get("users"))
        instance.save()
        return instance

PATCH請求views.py

class BookView(APIView):
    def patch(self, request):
        print(request.data)
        book_id = request.data["id"]
        book_info = request.data["book_info"]
        book_obj = Book.objects.filter(pk=book_id).first()
        serializer = BookSerializer(book_obj, data=book_info, partial=True)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.validated_data)
        else:
            return Response(serializer.errors)

 

驗證

如果需要對一些欄位進行自定義的驗證—DRF也給我們提供了鉤子方法

 

單個欄位的驗證

class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(max_length=32)
    # 省略了一些欄位 跟上面程式碼裡一樣的
    # 。。。。。
    def validate_title(self, value):
        if "python" not in value.lower():
            raise serializers.ValidationError("標題必須含有Python")
        return value

 

多個欄位的驗證

class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(max_length=32)
    CHOICES = ((1, "Linux"), (2, "Django"), (3, "Python"))
    chapter = serializers.ChoiceField(choices=CHOICES, source="get_chapter_display", read_only=True)
    w_chapter = serializers.IntegerField(write_only=True)
    pub_time = serializers.DateField()
    date_added = serializers.DateField(write_only=True)
    # 新增了一個上架時間欄位  
    # 省略一些欄位。。都是在原基礎程式碼上增加的
    # 。。。。。。

    # 對多個欄位進行驗證 要求上架日期不能早於出版日期 上架日期要大
    def validate(self, attrs):
        if attrs["pub_time"] > attrs["date_added"]:
            raise serializers.ValidationError("上架日期不能早於出版日期")
        return attrs

 

驗證器 validators

def my_validate(value):
    if "敏感詞彙" in value.lower:
        raise serializers.ValidationError("包含敏感詞彙,請重新提交")
    return value


class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(max_length=32, validators=[my_validate])
    # 。。。。。。

 

ModelSerializer

已經清楚了Serializer的用法,會發現我們所有的序列化跟我們的模型都緊密相關~

那麼,DRF也給我們提供了跟模型緊密相關的序列化器~~ModelSerializer~~

  -- 它會根據模型自動生成一組欄位

  -- 它簡單的預設實現了.update()以及.create()方法

 

定義一個ModelSerializer序列化器

定義ModelSerializer

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = "__all__"
        # fields = ["id", "title", "pub_time"]
        # exclude = ["user"]
        # 分別是所有欄位 包含某些欄位 排除某些欄位

 

外來鍵關係的序列化

注意:當序列化類MATE中定義了depth時,這個序列化類中引用欄位(外來鍵)則自動變為只讀

外來鍵關係序列化

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = "__all__"
        # fields = ["id", "title", "pub_time"]
        # exclude = ["user"]
        # 分別是所有欄位 包含某些欄位 排除某些欄位
        depth = 1
# depth 代表找巢狀關係的第幾層

 

自定義欄位

我們可以宣告一些欄位來覆蓋預設欄位,來進行自定製~

比如我們的選擇欄位,預設顯示的是選擇的key,我們要給使用者展示的是value。

自定義欄位

class BookSerializer(serializers.ModelSerializer):
    chapter = serializers.CharField(source="get_chapter_display", read_only=True)
    
    class Meta:
        model = Book
        fields = "__all__"
        # fields = ["id", "title", "pub_time"]
        # exclude = ["user"]
        # 分別是所有欄位 包含某些欄位 排除某些欄位
        depth = 1

 

Meta中其它關鍵字引數

Meta中引數

class BookSerializer(serializers.ModelSerializer):
    chapter = serializers.CharField(source="get_chapter_display", read_only=True)

    class Meta:
        model = Book
        fields = "__all__"
        # fields = ["id", "title", "pub_time"]
        # exclude = ["user"]
        # 分別是所有欄位 包含某些欄位 排除某些欄位
        depth = 1
        read_only_fields = ["id"]
        extra_kwargs = {"title": {"validators": [my_validate,]}}

 

post以及patch請求

由於depth會讓我們外來鍵變成只讀,所以我們再定義一個序列化的類,其實只要去掉depth就可以了~~

post/patch請求序列化類

class BookSerializer(serializers.ModelSerializer):
    chapter = serializers.CharField(source="get_chapter_display", read_only=True)

    class Meta:
        model = Book
        fields = "__all__"
        # fields = ["id", "title", "pub_time"]
        # exclude = ["user"]
        # 分別是所有欄位 包含某些欄位 排除某些欄位
        read_only_fields = ["id"]
        extra_kwargs = {"title": {"validators": [my_validate,]}}

 

SerializerMethodField

外來鍵關聯的物件有很多欄位我們是用不到的都傳給前端會有資料冗餘就需要我們自己去定製序列化外來鍵物件的哪些欄位~~

SerializerMethodField

class BookSerializer(serializers.ModelSerializer):
    chapter = serializers.CharField(source="get_chapter_display", read_only=True)
    user = serializers.SerializerMethodField()
    publisher = serializers.SerializerMethodField()

    def get_user(self, obj):
        # obj是當前序列化的book物件
        users_query_set = obj.user.all()
        return [{"id": user_obj.pk, "name": user_obj.name} for user_obj in users_query_set]

    def get_publisher(self, obj):
        publisher_obj = obj.publisher
        return {"id": publisher_obj.pk, "title": publisher_obj.title}

    class Meta:
        model = Book
        fields = "__all__"
        # fields = ["id", "title", "pub_time"]
        # exclude = ["user"]
        # 分別是所有欄位 包含某些欄位 排除某些欄位
        read_only_fields = ["id"]
        extra_kwargs = {"title": {"validators": [my_validate,]}}

 

用ModelSerializer改進上面Serializer的完整版

ModelSerializer

class BookSerializer(serializers.ModelSerializer):
    dis_chapter = serializers.SerializerMethodField(read_only=True)
    users = serializers.SerializerMethodField(read_only=True)
    publishers = serializers.SerializerMethodField(read_only=True)

    def get_users(self, obj):
        # obj是當前序列化的book物件
        users_query_set = obj.user.all()
        return [{"id": user_obj.pk, "name": user_obj.name} for user_obj in users_query_set]

    def get_publishers(self, obj):
        publisher_obj = obj.publisher
        return {"id": publisher_obj.pk, "title": publisher_obj.title}

    def get_dis_chapter(self, obj):
        return obj.get_chapter_display()

    class Meta:
        model = Book
        # fields = "__all__"
        # 欄位是有序的
        fields = ["id", "title","dis_chapter", "pub_time", "publishers", "users","chapter", "user", "publisher"]
        # exclude = ["user"]
        # 分別是所有欄位 包含某些欄位 排除某些欄位
        read_only_fields = ["id", "dis_chapter", "users", "publishers"]
        extra_kwargs = {"title": {"validators": [my_validate,]}, "user": {"write_only": True}, "publisher": {"write_only": True},
                        "chapter": {"write_only": True}}