1. 程式人生 > >Serializers 序列化組件

Serializers 序列化組件

integer span attrs 更新 ise serial 冗余 date() dumps

為什麽要用序列化組件

當我們做前後端分離的項目~~我們前後端交互一般都選擇JSON數據格式,JSON是一個輕量級的數據交互格式。

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

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

Django的序列化方法

技術分享圖片
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)
.values 序列化結果 技術分享圖片
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)
django serializers

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)
第二步 序列化對象

外鍵關系的序列化

技術分享圖片
# by gaoxin
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()方法~~

技術分享圖片
# 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
反序列化serializer.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)
反序列化views.py

當前端給我們發送patch請求的時候,前端傳給我們用戶要更新的數據,我們要對數據進行部分驗證~~

技術分享圖片
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請求serializers.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)
PATCH請求views.py

驗證

如果我們需要對一些字段進行自定義的驗證~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
多個字段的驗證 技術分享圖片
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])
    # 。。。。。。
    
驗證器 validators

ModelSerializer

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

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

  -- 它會根據模型自動生成一組字段

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

定義一個ModelSerializer序列化器

技術分享圖片
class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = "__all__"
        # fields = ["id", "title", "pub_time"]
        # exclude = ["user"]
        # 分別是所有字段 包含某些字段 排除某些字段
定義ModelSerializer

外鍵關系的序列化

註意:當序列化類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中其它關鍵字參數

技術分享圖片
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,]}}
Meta中參數

post以及patch請求

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

技術分享圖片
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,]}}
post/patch請求序列化類

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,]}}
SerializerMethodField

用ModelSerializer改進上面Serializer的完整版

技術分享圖片
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}}
ModelSerializer

Serializers 序列化組件