DRF之序列化

一、Django的序列化方法

1、为什么要用序列化组件

做前后端分离的项目,我们前后端数据交互一般都选择JSON,JSON是一个轻量级的数据交互格式。
那么我们给前端数据的时候都要转成json格式,那就需要对我们从数据库拿到的数据进行序列化。


2、表的构建

CHOICES = ((1, "python"), (2, "linux"), (3, "go"))

# 书籍表
class Book(models.Model):
    title = models.CharField(max_length=64)
    category = models.IntegerField(choices=CHOICES)  # 书籍分类
    pub_time = models.DateField()
    publisher = models.ForeignKey(to="Publisher")
    authors = models.ManyToManyField(to="Author")

    class Meta:
        verbose_name = '书籍'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.title


# 出版社表
class Publisher(models.Model):
    title = models.CharField(max_length=64)

    class Meta:
        verbose_name = '出版社'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.title


# 作者表
class Author(models.Model):
    name = models.CharField(max_length=32)
    class Meta:
        verbose_name = '作者'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name
View Code

3、希望构建的字典格式

book_list = [
    {
        "id": 1,
        "title": "",
        "publisher": {  # 外键
            "id": 1
            "title": ""
        },
        "authors": [{}, {}]  # 多对多
    },
    {
        "id": 2,
        "title": "",
        "publisher": {  # 外键
            "id": 1
            "title": ""
        },
        "authors": [{}, {}]  # 多对多
    },

]

4、方法一

在Django中使用Json模块序列化
class BooksView(views.View):
    def get(self, request):
        book_queryset = Book.objects.values("id", "title", "pub_time", "publisher")
        book_list = list(book_queryset)  # 把queryset类型转换成列表
        # 如果我们需要取外键关联的字段信息 需要循环获取外键 再去数据库查然后拼接成我们想要的
        ret = []
        for book in book_list:
            publisher_obj = Publisher.objects.filter(id=book["publisher"]).first()
            # 修改原字典中的publisher对应的值
            book["publisher"] = {
                "id": publisher_obj.id,
                "title": publisher_obj.title
            }
            # 把新的字典追加到一个空列表中
            ret.append(book)
        ret = json.dumps(ret, ensure_ascii=False, cls=MyJson)
        return HttpResponse(ret)
View Code

5、方法二

使用JsonResponse,自动帮我们重写了JSONEncoder里面的default方法,解决时间字段的问题
class BooksView(views.View):
    def get(self, request):
        book_queryset = Book.objects.values("id", "title", "pub_time", "publisher")
        book_list = list(book_queryset)  # 把queryset类型转换成列表
        # 如果我们需要取外键关联的字段信息 需要循环获取外键 再去数据库查然后拼接成我们想要的
        ret = []
        for book in book_list:
            publisher_obj = Publisher.objects.filter(id=book["publisher"]).first()
            # 修改原字典中的publisher对应的值
            book["publisher"] = {
                "id": publisher_obj.id,
                "title": publisher_obj.title
            }
            # 把新的字典追加到一个空列表中
            ret.append(book)
        return JsonResponse(ret, safe=False, json_dumps_params={'ensure_ascii': False})
View Code

6、方法三

使用Django自带的序列化模块
from django.core import serializers


# 能够得到我们要的效果 结构有点复杂
class BooksView(views.View):
    def get(self, request):
        book_queryset = Book.objects.all()
        ret = serializers.serialize("json", book_queryset, ensure_ascii=False)
        return HttpResponse(ret)
View Code

二、DRF序列化的介绍

1、介绍

下载DRF模块:pip install djangorestframework
导入:
    from rest_framework.views import APIView
    from rest_framework.response import Response

首先,我们要用DRF的序列化,就要遵循DRF框架的一些标准,
  -- Django我们CBV继承类是View,现在DRF我们要用APIView
  -- Django中返回的时候我们用HTTPResponse,JsonResponse,render ,DRF我们用Response

2、APIView跟View区别

-- APIView继承了View
-- APIView的as_view方法实现了csrf中间件的豁免(用DRF不需要再把settings的csrf中间件注释掉了)
-- 重新封装了request
    request._request可以拿到旧的request
    request.query_params  旧的request.GET
    request.data    除了GET请求外的所有的数据(已经序列化好的数据)
    validated_data是通过校验的数据,也会封装到data里面
-- DRF有自己的序列化模块
    from rest_framework import serializers

三、DRF序列化步骤

1、声明序列化器

# serializers.py文件

from rest_framework import serializers
from libsys.models import Book


CHOICES = ((1, "python"), (2, "linux"), (3, "go"))


# 继承serializers.Serializer
class PublisherSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    title = serializers.CharField(max_length=64)


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


# 字段的声明和models相类似
class BookSerializer(serializers.Serializer):
    # POST校验的时候required=False声明不需要校验这个字段
    id = serializers.IntegerField(required=False)

    title = serializers.CharField(max_length=64)
    pub_time = serializers.DateField()

    # 选择字段,显示的是数字
    # category = serializers.ChoiceField(choices=CHOICES)
    # 把选择字段显示成字符类型,source参数后面跟的是ORM操作
    # read_only=True表示这个字段只在渲染前端的时候使用
    category = serializers.CharField(source='get_category_display', read_only=True)
    # write_only=True表示这个字段只在POST提交数据,做校验的时候使用
    post_category = serializers.IntegerField(write_only=True)

    # 外键需要设置额外的序列化器对它进行序列化
    publisher = PublisherSerializer(read_only=True)
    publisher_id = serializers.IntegerField(write_only=True)

    # 多对多字段需要设置额外的序列化器对它进行序列化,且声明many=True
    authors = AuthorSerializer(many=True, read_only=True)
    author_list = serializers.ListField(write_only=True)

    def create(self, validated_data):  # validated_data是通过校验的数据,最后也会封装到data里面
        #通过ORM操作给Book表增加数据
        book_obj = 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.authors.add(*validated_data["author_list"])
        return book_obj
View Code

2、在视图函数中调用

from libsys.models import Book, Publisher, Author
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializers import BookSerializer

# Create your views here.


# 书籍列表
class BookView(APIView):
    def get(self, request):
        book_queryset = Book.objects.all()
        # 声明一个序列化器
        # 用序列化器去序列化queryset(queryset有多个对象的时候,需要声明many=True)
        # 把数据提交到序列化器,跟序列化器的字段进行匹配,匹配成功就进行序列化
        ser_obj = BookSerializer(book_queryset, many=True)
        return Response(ser_obj.data)

    # 新增书籍
    def post(self, request):
        # 前端传过来的数据应该也是这样的
        # {"id""title",
        #       category: 1
        #       publisher: 1
        #       authors: [1, 2]
        #  }

        # 获取前端传过来的数据
        book_obj = request.data
        # 用序列化器做校验
        ser_obj = BookSerializer(data=book_obj)
        if ser_obj.is_valid():
            # 校验通过,新增书籍
            ser_obj.save()  # 这里的save方法会去调用序列化器的create方法
            print(ser_obj.validated_data)  # validated_data是通过校验的数据,也会封装到data里面
            return Response(ser_obj.data)
        # 校验不通过返回错误信息
        return Response(ser_obj.errors)
View Code

3、注意事项

1. 外键和多对多字段的序列化需要额外再设置序列化器
2. 序列化
    序列化器的参数是queryset和many
3. 反序列化
    序列化器的参数是data=提交上来的数据
4. 序列化器字段类型不统一的情况
    反序列化要用的一些字段通过一些参数跟序列化区分开
    -- required=False
    -- read_only=True
    -- write_only=True

5. 反序列化的验证
    is_valid()  --> 校验数据
    save()        --> 调用序列化器的create方法

4、DRF的钩子方法

5、小结

-- 序列化
    -- 声明一个序列化器
        class BookSerializer(serializers.Serializer):
            id = serializers.IntegerField(required=False)
            title = serializers.CharField(max_length=32)
            pub_time = serializers.DateField()
    -- 视图里序列化我们的queryset
        ser_obj = BookSerializer(queryset, many=True)
        return Response(ser_obj.data)
    -- 实现流程
        -- 如果指定了many=True
        -- 把queryset当成可迭代对象去循环 得到每个模型对象
        -- 把每个模型对象放入序列号器进行序列化
        -- 进行字段匹配 匹配上的字段进行序列化 匹配不上丢弃
        -- 必须满足序列化的所有字段要求
-- 反序列化
    -- 获取前端传过来的数据
    -- 用序列化器进行校验
        ser_obj = BookSerializer(data=request.data)
        if ser_obj.is_valid():
            ser_obj.save()# 调用create方法
            return Response(ser_obj.data)
        else:
            return Response(ser_obj.errors)
    -- 写create方法
        在create方法里用ORM操作创建新对象

猜你喜欢

转载自www.cnblogs.com/Zzbj/p/10100284.html