为什么要用序列化
当我们做前后端分离的项目时,前后端交互一般都是JSON格式的数据,那么我们给前端的数据就要转为JSON格式,就需要我们拿到数据库后的数据进行序列化。在看DRF的序列化之前,先来看看django的序列化
model.py
序列化
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请求,就返回如下的数据
序列化返回给前端的数据
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)