DRF序列化

Serializers 序列化组件

什么要用序列化组件 :

  在我们做前后端分离的项目时候, 我们前后端交互一般都选择JSON数据格式, JSON是一个轻量级的数据交互格式.

  那么我们给前端数据的时候都要转成JSON格式, 那就需要对我们从数据库拿到的数据进行序列化.

  


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)
.value序列化结果
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, reder, 而DRF我们使用Response来接受.

序列化 :

class Book(models.Model):
    title = models.CharField(max_length=32)
    CHIOCES = ((1, "python"), (2, "linux"), (3, "go"))
    category = models.IntegerField(choices=CHIOCES)
    pub_time = models.DateField()
    publisher = models.ForeignKey(to="Publisher")
    author = models.ManyToManyField(to="Author")
mdoel表的字段
class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False)  # required 为False时, 反序列化不做校验
    title = serializers.CharField(max_length=32, validators=[my_validate])
    pub_time = serializers.DateField()
    CHOICES = ((1, "Linux"), (2, "Django"), (3, "Python"))
    category = serializers.CharField(choices=CHOICES)

    publisher = PublisherSerializer(read_only=True)
    # 多对多有many参数
    author = AuthorSerializer(many=True, read_only=True)
声明序列化器
from rest_framework.views import APIView
from app01 import models
from .serializers import BookSerializer
from rest_framework.response import Response

class BookView(APIView):
    def get(self, request):
        book_queryset = models.Book.objects.all()
        # 用序列化器进行序列化
        ser_obj = BookSerializer(book_queryset, many=True)

        return Response(ser_obj.data)
序列化对象

外键关系的序列化 :

from rest_framework import serializers
from app01 import models


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


class AuthorSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    name = serializers.CharField(max_length=32)


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, )
    pub_time = serializers.DateField()

    publisher = PublisherSerializer(read_only=True)
    # 多对多字段有many=True参数
    author= AuthorSerializer(many=True, read_only=True)
外键关系的序列化器

反序列化 :

  当前端给我们发送post请求时候, 前端给我们传来的数据, 我们要经过一些校验然后保存到数据库. 这些校验以及爆粗工作, DRF的serializer也给我们提供了一些方法了.

  首先, 我们要写反序列化需要使用的一些字段, 有些字段(外键, 多对多, choices)需要和序列化字段分开.

  serializer提供了 .is_valid() 和 .save()方法

from django.db import models


class Book(models.Model):
    title = models.CharField(max_length=32)
    CHIOCES = ((1, "python"), (2, "linux"), (3, "go"))
    category = models.IntegerField(choices=CHIOCES)
    pub_time = models.DateField()
    publisher = models.ForeignKey(to="Publisher")
    author = models.ManyToManyField(to="Author")


class Publisher(models.Model):
    title = models.CharField(max_length=32)


class Author(models.Model):
    name = models.CharField(max_length=32)
全部model
class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False)  # required 为False时, 反序列化不做校验
    title = serializers.CharField(max_length=32, validators=[my_validate])
    pub_time = serializers.DateField()
    category = serializers.CharField(source="get_category_display", read_only=True)
    # 自定义一个字段只用来反序列化接收使用
    post_category = serializers.IntegerField(write_only=True)

    publisher = PublisherSerializer(read_only=True)
    publisher_id = serializers.IntegerField(write_only=True)
    # 多对多有many参数
    author = AuthorSerializer(many=True, read_only=True)
    author_list = serializers.ListField(write_only=True)

    # 新增数据要重写的create方法
    def create(self, validated_data):
        # validated_data是验证通过的数据
        # 通过ORM操作给Book表增加数据
        # 添加除多对多字段的所有字段
        book_obj = models.Book.objects.create(
            title=validated_data["title"],
            pub_time=validated_data["pub_time"],
            category=validated_data["post_category"],
            publisher_id=validated_data["publisher_id"],
        )
        # 添加多对多字段
        book_obj.author.add(*validated_data["author_list"])
        return book_obj
反序列化序列化器
class BookView(APIView):
    def get(self, request):
        book_queryset = models.Book.objects.all()
        # 用序列化器进行序列化
        ser_obj = BookSerializer(book_queryset, many=True)

        return Response(ser_obj.data)

    def post(self, request):
        # 接收前端传过来的数据
        book_obj = request.data
        # 对前端传过来的数据使用自定义序列化方法进行校验(是否合法等)
        ser_obj = BookSerializer(data=book_obj)
        # 如果校验通过做些什么
        if ser_obj.is_valid():
            ser_obj.save()
            # validated_data是校验通过之后的数据
            return Response(ser_obj.validated_data)
        # 验证不通过返回错误信息
        return Response(ser_obj.errors)
反序列化view.py

  当前端给我们发送put或者delete请求的时候, 前端给我们用户需要更新的数据, 我们要对数据进行部分验证.

class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False)  # required 为False时, 反序列化不做校验
    title = serializers.CharField(max_length=32, validators=[my_validate])
    pub_time = serializers.DateField()
    category = serializers.CharField(source="get_category_display", read_only=True)
    # 自定义一个字段只用来反序列化接收使用
    post_category = serializers.IntegerField(write_only=True)

    publisher = PublisherSerializer(read_only=True)
    publisher_id = serializers.IntegerField(write_only=True)
    # 多对多有many参数
    author = AuthorSerializer(many=True, read_only=True)
    author_list = serializers.ListField(write_only=True)

    # 新增数据要重写的create方法
    def create(self, validated_data):
        # validated_data是验证通过的数据
        # 通过ORM操作给Book表增加数据
        # 添加除多对多字段的所有字段
        book_obj = models.Book.objects.create(
            title=validated_data["title"],
            pub_time=validated_data["pub_time"],
            category=validated_data["post_category"],
            publisher_id=validated_data["publisher_id"],
        )
        # 添加多对多字段
        book_obj.author.add(*validated_data["author_list"])
        return book_obj

    # 更新数据要重写update方法
    def update(self, instance, validated_data):
        # instance 是要更新的对象
        # 对除多对多字段以外的字段进行更新, 并设置当前已存在的数据为默认值
        instance.title = validated_data.get("title", instance.title)
        instance.pub_time = validated_data.get("pub_time", instance.pub_time)
        instance.category = validated_data.get("post_category", instance.category)
        instance.publisher_id = validated_data.get("publisher_id", instance.publisher_id)
        # 判断前端传过来的数据是否含有author_list字段, 如果有则更新, 没有就不变动
        if validated_data.get("author_list"):
            instance.author.set(validated_data["author_list"])
        instance.save()
        return instance
put或者delete的序列化器
from rest_framework.views import APIView
from app01 import models
from .serializers import BookSerializer
from rest_framework.response import Response

class BookEditView(APIView):
    def get(self, request, edit_id):
        book_obj = models.Book.objects.filter(id=edit_id).first()
        ser_obj = BookSerializer(book_obj)
        return Response(ser_obj.data)

    def put(self, request, edit_id):
        book_obj = models.Book.objects.filter(id=edit_id).first()
        ser_obj = BookSerializer(instance=book_obj, data=request.data, partial=True)
        if ser_obj.is_valid():
            ser_obj.save()
            return Response(ser_obj.validated_data)
        return Response(ser_obj.errors)

    def delete(self, request, edit_id):
        book_obj = models.Book.objects.filter(id=edit_id).first()
        if not book_obj:
            return Response("删除对象不存在!")
        book_obj.delete()
        return Response("删除成功了呢!")
put或者delete的view.py

验证 :

  如果我们需要对一些字段进行自定义的验证, DRF也给我们提供了钩子方法.

def my_validate(value):
    if "敏感信息" in value.lower():
        raise serializers.ValidationError("存在敏感词汇!!!")


class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False)  # required 为False时, 反序列化不做校验
    title = serializers.CharField(max_length=32, validators=[my_validate])
自定义验证器
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(required=False)  # required 为False时, 反序列化不做校验
    title = serializers.CharField(max_length=32, validators=[my_validate])
    pub_time = serializers.DateField()
    category = serializers.CharField(source="get_category_display", read_only=True)
    # 自定义一个字段只用来反序列化接收使用
    post_category = serializers.IntegerField(write_only=True)

    publisher = PublisherSerializer(read_only=True)
    publisher_id = serializers.IntegerField(write_only=True)
    # 多对多有many参数
    author = AuthorSerializer(many=True, read_only=True)
    author_list = serializers.ListField(write_only=True)




# 对前端传过来的数据进行条件控制
def validate(self, attrs):
      # 相当于钩子函数
      # attrs是一个字典, 含有传过来的所有字段
       if "python" in attrs["title"].lower() and attrs["post_category"] == 1:
           return attrs
       else:
           raise serializers.ValidationError("分类或标题不匹配")
多个字段的验证

ModelSerizlizer :

当我们清楚了Srrializer的用法之后, 会发现所有的序列化跟我们的模型都紧密相关.

既然如此, DRF也给我们提供了跟模型紧密相关的序列化器: ModelSerializer

  此序列化器会根据模型自动生成一组字段

  并且简单的默认实现了 .update()以及 .create()方法

定义一个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 代表找嵌套关系的第几层

自定义字段 :

  可以通过声明字段的防水来覆盖默认字段, 来进行自定制

  比如model中的choices字段, 默认显示的是选择的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):
    """
    继承ModelSerializer方法不需要在定义一个用来反序列化的字段
    """

    # 以下方法为自定义外键, 多对多, choices字段的显示内容

    # 根据自己方法里构建的数据返回给指定的字段, 钩子函数返回什么就展示什么
    publisher_info = serializers.SerializerMethodField(read_only=True)
    author_info = serializers.SerializerMethodField(read_only=True)
    category_info = serializers.SerializerMethodField(read_only=True)

    # SerializerMethodField提供的钩子函数, get_使用钩子函数的字段名
    # 此钩子函数的返回值会返回给SerializerMethodField方法
    # obj参数为调用序列化方法的QuerySet中的每个对象
    def get_publisher_info(self, obj):
        # publisher_query为每个序列化对象所关联的外键对象
        publisher_query = obj.publisher
        return {"id": publisher_query.id, "title": publisher_query.title}
    def get_author_info(self, obj):
        # author_query为每个要序列化对象所关联的多对多的全部对象
        author_query = obj.author.all()
        return [{"id": author.id, "name": author.name} for author in author_query]
    # choices字段, 将要序列化的对象直接调用一次display方法, 使其显示具体内容即可
    def get_category_info(self, obj):
        return obj.get_category_display()


    class Meta:
        model = models.Book
        fields = "__all__"
        # "depth解释  ↓↓↓"
        # 会查找一层外键关联的对象的所有(划重点)字段内容
        # 但会使这些所有的外键关系变成read_only = True, 反序列化外键关系会丢失
        # 一般情况下不常用
        # depth = 1

        # 加自定义操作的方法
        # 使publisher, author字段添加一个write_only参数, 是其只能进行反序列化使用
        extra_kwargs = {"publisher": {"write_only": True}, "author": {"write_only": True}, "category": {"write_only": True}}

  

原文地址:https://www.cnblogs.com/dong-/p/9971915.html