DRF--序列化

为什么要用序列化

当我们做前后端分离的项目时,前后端交互一般都是JSON格式的数据,那么我们给前端的数据就要转为JSON格式,就需要我们拿到数据库后的数据进行序列化。在看DRF的序列化之前,先来看看django的序列化

from django.db import models

# Create your models here.

__all__ = ["Book", "Publisher", "Author"]


class Book(models.Model):
    title = models.CharField(max_length=32)
    CHOICES = ((1, "python"), (2, "Liunux"), (3, "Go"))
    category = models.IntegerField(choices=CHOICES)
    pub_time = models.DateField()
    publisher = models.ForeignKey(to="Publisher")
    authors = models.ManyToManyField(to="Author")


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

    def __str__(self):
        return self.title


class Author(models.Model):
    name = models.CharField(max_length=32)

    def __str__(self):
        return self.name
model.py
from django.http import JsonResponse
from django.shortcuts import render
from django.views import View
from djangoDemo.models import Book, Publisher


# 第一版用values方法取数据
class BookView(View):
    def get(self, request):
        book_queryset = Book.objects.values("id", "title", "pub_time", "publisher")
        book_list = list(book_queryset)
        ret = []
        for book in book_list:
            book["publisher"] = {
                "id": book["publisher"],
                "title": Publisher.objects.filter(id=book["publisher"]).first().title,
            }
            ret.append(book)
        # ret = json.dumps(book_list, ensure_ascii=False)
        # return HttpResponse(ret)
        return JsonResponse(ret, safe=False, json_dumps_params={"ensure_ascii": False})
序列化

DRF的序列化

从上面的例子中可以看出来,这样我们序列化出来的数据,写法很麻烦。所以我们用DRF的序列化,要用别人的序列化就要遵守别人的规则,首先需要在app里注册

INSTALLED_APPS = [
    'django.contrib.admin',

    ...
    
    'rest_framework',
]

注册好了之后要声明序列化类,在当前app下创建一个py文件

 1 from rest_framework import serializers
 2 
 3 #  出版社的序列化器,前面的变量要和model里的变量名一样
 4 class PublisherSerializer(serializers.Serializer):
 5     id = serializers.IntegerField()
 6     title = serializers.CharField(max_length=32)
 7 
 8 
 9 class AuthorSerializer(serializers.Serializer):
10     id = serializers.IntegerField()
11     name = serializers.CharField(max_length=32)
12 
13 
14 class BookSerializer(serializers.Serializer):
15     id = serializers.IntegerField()
16     title = serializers.CharField(max_length=32)
17     pub_time = serializers.DateField()
18     category = serializers.CharField(source="get_category_display")  # 选择的需要指定source
19 
20     publisher = PublisherSerializer()  # 一对多的表
21     authors = AuthorSerializer(many=True)  # 多对多的表需要指定many=True

上面我们就写好了序列化的类,注意:匹配上的字段进行序列化,匹配不上则丢弃,所以前端需要哪些字段就写哪些,如果不写的就不序列化

外键关系的序列化是嵌套的序列化器对象 
注意many=True

然后再视图函数里写序列化对象

from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from djangoDemo.models import Book  # 导入表
from .serializers import BookSerializer


class BookView(APIView):
    def get(self, request):
        book_queryset = Book.objects.all()
        # 拿出来的是一个queryset,用序列化器进行序列化
        ser_obj = BookSerializer(book_queryset, many=True)
        return Response(ser_obj.data)  # 序列化后的数据在data里

注意:当查询出的数据是一个queryset时,需要加many=True,内部会认为是一个可迭代的对象,会去循环。当查询出的是一条数据时,不需要加many=True,会报错

当我们访问这个接口时,返回的数据就是如下的格式

{
        "id": 1,
        "title": "python从入门到放弃",
        "pub_time": "2019-09-10",
        "category": "python",
        "publisher": {
            "id": 1,
            "title": "清华"
        },
        "authors": [
            {
                "id": 1,
                "name": "马云"
            },
            {
                "id": 2,
                "name": "刘亦菲"
            }
        ]
    }

反序列化

当我们进行post请求的时候,我们需要定义数据格式,然后让前端的妹子传给我们对应的格式。格式确定之后我们还要校验前端妹子传过来的格式和字段,比如字段的类型还有长度,我们不能相信前端妹子的话,妹子说“我没事”。难道就真没事了吗?所以数据校验是必不可少的。

数据格式当然是json格式的,那怎么校验数据呢,在上面我们写了个序列化类,来序列化返回给前端的数据,我们也可以用来校验前端传给我们的数据。因为id是自动递增的,所以前端不需要传,我们也不需要校验,可以加个参数required=False,表示只序列化不反序列化。还有些字段,比如上面的category字段,我们序列化的时候返回的是后面的汉字,而反序列化的时候,我们希望是前面的数字,所以我们需要重写这个字段,如果字段里有read_only=True,表示只序列化。如果是write_only=True,表示只反序列化

返回的数据是上面格式的,我们如果新增数据,希望数据是下面这个格式的

{
      
        "title": "HTML",
        "pub_time": "2019-09-10",
        "post_category": 1,
        "publisher_id":1,
        "author_list": [1,2]
}

注意,前面的key是序列化器里对应的字段

先来改写序列化器

class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False)  # 只序列化,不走校验
    title = serializers.CharField(max_length=32)
    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)  # 一对多的表  只序列化用
    authors = AuthorSerializer(many=True, read_only=True)  # 多对多的表需要指定many=True 只序列化用

    publisher_id = serializers.IntegerField(write_only=True)  # 只反序列化用
    author_list = serializers.ListField(write_only=True)  # 只反序列化用

    def create(self, validated_data):
        # validated_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

在来改写视图函数,新增post请求

class BookView(APIView):
    def get(self, request):
        book_queryset = Book.objects.all()
        # 拿出来的是一个queryset,用序列化器进行序列化
        ser_obj = BookSerializer(book_queryset, many=True)
        return Response(ser_obj.data)  # 序列化后的数据在data里

    def post(self, request):
        # 确定数据类型以及数据结构
        # 对前端传来的数据进行校验
        book_obj = request.data  # post传来的数据
        ser_obj = BookSerializer(data=book_obj) # 有data参数,表示反序列化
        if ser_obj.is_valid():
            ser_obj.save()
            return Response(ser_obj.validated_data)
        return Response(ser_obj.errors)  # 返回错误

这样,当我们提交像上面一样的数据格式之后,在看get请求,就返回如下的数据

[
    {
        "id": 1,
        "title": "python从入门到放弃",
        "pub_time": "2019-09-10",
        "category": "python",
        "publisher": {
            "id": 1,
            "title": "清华"
        },
        "authors": [
            {
                "id": 1,
                "name": "马云"
            },
            {
                "id": 2,
                "name": "刘亦菲"
            }
        ]
    },
    {
        "id": 2,
        "title": "Linux跑路",
        "pub_time": "2019-09-18",
        "category": "Liunux",
        "publisher": {
            "id": 2,
            "title": "北大"
        },
        "authors": [
            {
                "id": 2,
                "name": "刘亦菲"
            }
        ]
    },
    {
        "id": 3,
        "title": "HTML",
        "pub_time": "2019-09-10",
        "category": "python",
        "publisher": {
            "id": 1,
            "title": "清华"
        },
        "authors": [
            {
                "id": 1,
                "name": "马云"
            },
            {
                "id": 2,
                "name": "刘亦菲"
            }
        ]
    }
]
序列化返回给前端的数据

put反序列化

 上面的序列化是post请求的,那我们修改数据的时候可能是只对某一个字段进行修改

路由:

urlpatterns = [
    url(r'^book/$', BookView.as_view()),
    url(r'^book/(?P<id>d+)', BookEditView.as_view()),
]

在 BookSerializer 序列化类里添加一个update方法

def update(self, instance, validated_data):
    # instance 更新的book_obj对象
    # validated_data 校验通过的数据
    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)
    if validated_data.get("author_list"):  # 可能有多个值
        instance.author.set(validated_data["author_list"])
    instance.save()  # 保存
    return instance

因为不知道修改的是哪个字段,所以都要写

在写put请求

class BookEditView(APIView):
    def get(self, request, id):
        book_obj = Book.objects.filter(id=id).first()
        ser_obj = BookSerializer(book_obj)  # 查询出的是一条数据,不需要加 many=True
        return Response(ser_obj.data)

    def put(self, request, id):
        book_obj = Book.objects.filter(id=id).first()
        # instance必传,data=request.data前端传的参数,partial=True部分修改
        ser_obj = BookSerializer(instance=book_obj, data=request.data, partial=True)
        if ser_obj.is_valid():
            ser_obj.save()
            return Response(ser_obj.data)  # 返回数据,注意不是ser_obj.validated_data
        return Response(ser_obj.errors)
原文地址:https://www.cnblogs.com/zouzou-busy/p/11559739.html