Django REST Framework序列化器

Django序列化和json模塊的序列化

從數據庫中取出數據後,雖然不能直接將queryset和model對象以及datetime類型序列化,但均可以將其轉化成能夠序列化的類型,再序列化。python

功能需求都能作到,可是比較麻煩,每次須要手動實現。且取出的數據還須要進行轉化,好比某些字段在存儲時將漢字轉化成數字,取出來後要將數字轉化成漢字,這都是須要每次手動操做。數據庫

Django REST Framework序列化

做用:json

  • 對數據庫中取出的數據序列化
  • 字段值的轉化(包括choice、外鍵、一對一關係、多對多關係)
  • 字段驗證(自動驗證和自定義驗證)

models.py函數

class Book(models.Model):
    title = models.CharField(max_length=32, verbose_name="圖書名稱")
    CHOICES = ((1, "Python"), (2, "Go"), (3, "Linux"))
    category = models.IntegerField(choices=CHOICES, verbose_name="圖書的類別")
    pub_time = models.DateField(verbose_name="圖書的出版日期")

    publisher = models.ForeignKey(to="Publisher", on_delete=None)
    author = models.ManyToManyField(to="Author")

    def __str__(self):
        return self.title

    class Meta:
        verbose_name_plural = "Book"
        db_table = verbose_name_plural


class Publisher(models.Model):
    title = models.CharField(max_length=32, verbose_name="出版社的名稱")

    def __str__(self):
        return self.title

    class Meta:
        verbose_name_plural = "Publisher"
        db_table = verbose_name_plural


class Author(models.Model):
    name = models.CharField(max_length=32, verbose_name="做者的姓名")

    def __str__(self):
        return self.name

    class Meta:
        verbose_name_plural = "Author"
        db_table = verbose_name_plural

serializers.Serializer序列化

views.pypost

from .seriallzers import BookSeriallzer # 自定義的問加減

class BookView(APIView):

    def get(self, request):
        book_list = Book.objects.all()
        ret = BookSeriallzer(book_list, many=True) # 序列化過程
        return Response(ret.data)

    def post(self, request):
        print("數據",request.data)
        serializer = BookSeriallzer(data=request.data) # 反序列化
        if serializer.is_valid():
            print("驗證經過")
            serializer.save() # save()方法保存數據庫,須要在序列化器裏自定義create方法
            return Response(serializer.data)
        else:
            return Response(serializer.errors)

seriallzers.pyui

from rest_framework import serializers

def my_validate(value):
    if "mingan" in value.lower():
        raise serializers.ValidationError("不能含有敏感信息")
    else:
        return value

class BookSeriallzer(serializers.Serializer):
    id = serializers.IntegerField(required=False) # required False的意思是 反序列化(存庫)的時候非必需
    title = serializers.CharField(max_length=32, validators=[my_validate]) # 此種驗證方式比局部鉤子優先級高
    CHOICES = ((1, "Python"), (2, "Go"), (3, "Linux"))
    category = serializers.ChoiceField(choices=CHOICES, source="get_category_display", read_only=True) # 只在序列化(讀庫)的時候起做用
    w_category = serializers.ChoiceField(choices=CHOICES, write_only=True) # 只在反序列化的時候用(存庫)
    pub_time = serializers.DateField()

    publisher = PublishSeriallzer(read_only=True)
    publisher_id = serializers.IntegerField(write_only=True)

    author = AuthorSeriallzer(many=True, read_only=True) # 經過many參數來區別是普通外鍵仍是多對多關係
    author_list = serializers.ListField(write_only=True)

    def create(self, validated_data):
        book = Book.objects.create(title=validated_data["title"],category=validated_data["w_category"],
                            pub_time=validated_data["pub_time"], publisher_id=validated_data["publisher_id"])
        book.author.add(*validated_data["author_list"])
        return book

    def update(self, instance, validated_data):
        "instance是views函數中傳來的book_obj"
        instance.title = validated_data.get("title", instance.title) # 若是已驗證的數據中沒有title字段,不更新,不會報錯
        instance.category = validated_data.get("w_category", instance.category)
        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("author_list"):
            instance.author.set(validated_data["author_list"]) # 更新多對多字段
        instance.save()
        return instance

    def validate_title(self, value):
        "局部鉤子,自定義對title字段的驗證"
        if "python" not in value.lower():
            raise serializers.ValidationError("標題必須含有python")
        return value

    def validate(self, attrs):
        "全局的校驗規則,能夠進行多字段聯合校驗"
        if attrs["w_category"] == 1 and attrs["publisher_id"] == 1:
            return attrs
        else:
            raise serializers.ValidationError("分類以及標題不符合要求")
驗證順序:validators屬性 -> 局部鉤子 -> 全局鉤子

這樣就實現了 序列化 和 反序列化的過程,包括特殊字段(時間類型、外鍵關係、chioce)轉化以及存庫時的驗證。spa

須要說明的是:rest

  • choice字段、外鍵因爲存取的需求不一致(存數字,取漢字、外鍵對應的其它字段),須要將序列化和反序列化的過程區分開來,分紅兩個變量名。用read_only和write_only區分
  • 驗證發生在反序列化的過程(存庫),會自動驗證字段類型、必填屬性、長度等條件。
  • 自定義驗證有三種方式,要注意前後順序。

可是,序列化和反序列化的過程很是不簡潔,寫了太多的東西。對象

serializers.ModelSerializer序列化

ModelSerializer類能夠與ORM映射表結合,自動對應序列化關係。blog

from .models import Book, Publisher

class BookSeriallzer(serializers.ModelSerializer):
    category_display = serializers.SerializerMethodField() # 讀取數據時的字段名(與"category"區分開來,下同)
    publisher_info = serializers.SerializerMethodField() # 配合 get_字段名() 鉤子,自定義經過外鍵取出的字段
    authors = serializers.SerializerMethodField()

    def get_category_display(self, obj):
        return obj.get_category_display()

    def get_publisher_info(self, obj):
        # obj 是每一個book對象
        publisher_obj = obj.publisher
        return {"id": publisher_obj.id, "title": publisher_obj.title} # 只取出來本身想要的數據,不會有其餘的冗餘數據

    def get_authors(self, obj):
        authors = obj.author.all()
        return [{"id": author.id, "name": author.name} for author in authors]

    class Meta:
        model = Book  # 對應ORM表
        # fields = ["id", "title", "pub_time", "category"] # 須要取出的字段
        fields = "__all__"  # 表示要取出全部字段
        # depth = 1   # 序列化的外鍵層級。若是不規定此值,全部的外鍵字段都只是id。有個缺點:會取出外鍵對應的全部數據,很是冗餘
        extra_kwargs = {
            "category": {"write_only": True}, # 「category」字段只寫
            "publisher": {"write_only": True},
            "author": {"write_only": True}
        }

這樣,咱們只須要將存取不一致的行爲定義好便可,其它字段均可以自動對應好。很是方便。

相關文章
相關標籤/搜索