RestFramework之序列化组件
一,自己手动序列化
class Book(APIView): # parser_classes = [FormParser, ] def get(self, request): book_list = models.Book.objects.all() temp = [] for book in book_list: temp.append({"title": book.title, "publish_date": book.publishDate}) # 如果字段较多,不方便 return JsonResponse(temp, safe=False) def post(self, request): print("post") print(request.data) return HttpResponse("ok")
二,使用django自带的序列化
from django.core.serializers import serialize # django自带的序列化
class Book(APIView): # parser_classes = [FormParser, ] def get(self, request): book_list = models.Book.objects.all() data = serialize("json", book_list) # 格式 print(data) return HttpResponse(data) def post(self, request): print("post") print(request.data) return HttpResponse("ok")
get请求访问时,返回的数据:
[{"model": "app01.book", "pk": 1, "fields": {"title": "\u897f\u6e38\u8bb0", "price": "123.00", "publishDate": null, "publish": 1, "authors": [1, 2]}},
{"model": "app01.book", "pk": 2, "fields": {"title": "\u4e09\u56fd\u6f14\u4e49", "price": "456.00", "publishDate": null, "publish": 2, "authors": [3]}},
{"model": "app01.book", "pk": 3, "fields": {"title": "\u7ea2\u697c\u68a6", "price": "745.12", "publishDate": null, "publish": 3, "authors": [2]}},
{"model": "app01.book", "pk": 4, "fields": {"title": "\u6c34\u6d52\u4f20", "price": "456.23", "publishDate": null, "publish": 1, "authors": [3]}}]
默认的是这种格式,而且没有校验的功能;
三,使用restframework的序列化组件
1, get请求返回数据
from rest_framework.response import Response from rest_framework import serializers class BookSerializer(serializers.Serializer): # 在这个类中写什么字段,就序列化什么字段 title = serializers.CharField(max_length=32) price = serializers.DecimalField(max_digits=5, decimal_places=2) publishDate = serializers.DateField() class Book(APIView): # parser_classes = [FormParser, ] def get(self, request): book_list = models.Book.objects.all() bs = BookSerializer(book_list, many=True) # 实例化上面那个类,传入一个queryset,mang=True表示很多序列化一条以上的数据 print(bs.data) return Response(bs.data) def post(self, request): print("post") print(request.data) return HttpResponse("ok")
可以看到bs.data是一个列表中 放的一个个的有序字典:
[OrderedDict([('title', '西游记'), ('price', '123.00'), ('publishDate', None)]),
OrderedDict([('title', '三国演义'), ('price', '456.00'), ('publishDate', None)]),
OrderedDict([('title', '红楼梦'), ('price', '745.12'), ('publishDate', None)]),
OrderedDict([('title', '水浒传'), ('price', '456.23'), ('publishDate', None)])]
在前端响应中的数据是:
[ { "title": "西游记", "price": "123.00", "publishDate": null }, { "title": "三国演义", "price": "456.00", "publishDate": null }, { "title": "红楼梦", "price": "745.12", "publishDate": null }, { "title": "水浒传", "price": "456.23", "publishDate": null } ]
2 ,post提交数据
使用book表:
class Book(models.Model): title = models.CharField(max_length=32) price = models.DecimalField(max_digits=5, decimal_places=2) publishDate = models.DateField(default="2018-09-18") publish = models.ForeignKey(to="Publish", on_delete=models.CASCADE, default=1) authors = models.ManyToManyField(to="Author", default=2) def __str__(self): return self.title
先写那个序列化类:
对于普通字段:
class BookSerializer(serializers.Serializer): title = serializers.CharField(max_length=32) price = serializers.DecimalField(max_digits=5, decimal_places=2) publishDate = serializers.DateField()
在写我们的视图:
class Book(APIView): # parser_classes = [FormParser, ] def get(self, request): book_list = models.Book.objects.all() bs = BookSerializer(book_list, many=True) print(bs.data) return Response(bs.data) def post(self, request): print(request.data) bs = BookSerializer(data=request.data, many=False) # many默认就是False,可以不写 if bs.is_valid(): # 校验 # 添加到数据库, 需要手动创建 models.Book.objects.create(**request.data) return Response(bs.data) # 创建成功,返回创建的数据 else: return Response(bs.errors) # 失败,返回错误信息
序列化的过程:
我们写了 bs = BookSerializer(data=request.data, many=False) 这句话的时候,
对于bs.data数据:
restframework序列化组件是这样做的:
temp = [ ]
for obj in book_list:
temp.append({
"title": obj.title, # 这里面的键就是我们写的字段
"price": obj.price,
"publishDate": obj.publishDate
})
json.dumps(temp),
那么,如果我们写了一对多,或者多对多字段,如:
class BookSerializer(serializers.Serializer): title = serializers.CharField(max_length=32) price = serializers.DecimalField(max_digits=5, decimal_places=2) publishDate = serializers.DateField() publish = serializers.CharField(max_length=32) # 一对多关系 authors = serializers.CharField(max_length=32) # 多对多关系
在序列化过程中,就会出现obj.publish, 结果是publish的对象; obj.authors, 结果是管理对象,即 app01.Author.None;
结果如下:
# get请求的结果
[ { "title": "西游记", "price": "123.00", "publishDate": null, "publish": "沙河出版社", # 显示的str方法的结果 "authors": "app01.Author.None" }, { "title": "三国演义", "price": "456.00", "publishDate": null, "publish": "西二旗出版社", "authors": "app01.Author.None" }, { "title": "红楼梦", "price": "745.12", "publishDate": null, "publish": "望京西出版社", "authors": "app01.Author.None" }, { "title": "水浒传", "price": "456.23", "publishDate": null, "publish": "沙河出版社", "authors": "app01.Author.None" }]
所以,对于一对多的字段:
class BookSerializer(serializers.Serializer): title = serializers.CharField(max_length=32) price = serializers.DecimalField(max_digits=5, decimal_places=2) publishDate = serializers.DateField() publish_email = serializers.CharField(max_length=32, source="publish.email") publish_name = serializers.CharField(max_length=32, source="publish.name") authors = serializers.CharField(max_length=32)
写上source之后,那里循环的时候就是采用的obj.source的内容了,所以前面的字段名称也就不一定要和表对齐了。
# get请求的结果
[ { "title": "西游记", "price": "123.00", "publishDate": null, "publish_email": "132", "publish_name": "沙河出版社", "authors": "app01.Author.None" }, { "title": "三国演义", "price": "456.00", "publishDate": null, "publish_email": "456", "publish_name": "西二旗出版社", "authors": "app01.Author.None" }]
对于多对多字段,我们当然也可以写source了,比如:
class BookSerializer(serializers.Serializer): title = serializers.CharField(max_length=32) price = serializers.DecimalField(max_digits=5, decimal_places=2) publishDate = serializers.DateField() publish_email = serializers.CharField(max_length=32, source="publish.email") publish_name = serializers.CharField(max_length=32, source="publish.name") authors = serializers.CharField(max_length=32, source="authors.all")
但是这样的话前端拿到的就是:
[ { "title": "西游记", "price": "123.00", "publishDate": null, "publish_email": "132", "publish_name": "沙河出版社", "authors": "<QuerySet [<Author: 觉先生>, <Author: 胡大炮>]>" # 前端根本不能处理queryset类型,所以也就没有意义了 }]
所以,多对多字段我们这么写:
class BookSerializer(serializers.Serializer): title = serializers.CharField(max_length=32) price = serializers.DecimalField(max_digits=5, decimal_places=2) publishDate = serializers.DateField() publish_email = serializers.CharField(max_length=32, source="publish.email") publish_name = serializers.CharField(max_length=32, source="publish.name") # authors = serializers.CharField(max_length=32) authors = serializers.SerializerMethodField() # 多对多字段 def get_authors(self, obj): # 这里的obj是循环中的书籍对象, 函数名称必须是 get_多对多字段名 ret = [] for author in obj.authors.all(): ret.append( {"name": author.name, "age": author.age} ) return ret
前端拿到的数据:
[ { "title": "西游记", "price": "123.00", "publishDate": null, "publish_email": "132", "publish_name": "沙河出版社", "authors": [ # 这样前端就可以处理了 { "name": "觉先生", "age": 18 }, { "name": "胡大炮", "age": 28 } ] } ]
多对多字段的序列话的时候:
循环时,如果时多对多字段,那么
temp.append({
"字段名(authors)": get_authors()函数的返回值
});

四, 使用ModelSerializer
from app01 import models from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import serializers class BookSerializer(serializers.ModelSerializer): class Meta: model = models.Book # fields = ["title", "price", "publishDate"] # 可以指定字段 fields = "__all__" # 所有字段 class Book(APIView): # parser_classes = [FormParser, ] def get(self, request): book_list = models.Book.objects.all() bs = BookSerializer(book_list, many=True) print(bs.data) return Response(bs.data) def post(self, request): print(request.data) bs = BookSerializer(data=request.data, many=False) # many默认就是False,可以不写 if bs.is_valid(): # 校验 bs.save() # 直接save() return Response(bs.data) # 创建成功,返回创建的数据 else: return Response(bs.errors) # 失败,返回错误信息
get请求数据,结果为:
[ { "id": 1, "title": "西游记", "price": "123.00", "publishDate": null, "publish": 1, # 默认取得都是主键 "authors": [ 1, 2 ] }, { "id": 2, "title": "三国演义", "price": "456.00", "publishDate": null, "publish": 2, "authors": [ 3 ] }, { "id": 3, "title": "红楼梦", "price": "745.12", "publishDate": null, "publish": 3, "authors": [ 2 ] } ]
我们也可以自己配置, 方法和上面直接使用Serializer一致:
class BookSerializer(serializers.ModelSerializer): class Meta: model = models.Book # fields = ["title", "price", "publishDate"] # 可以指定字段 fields = "__all__" # 所有字段 publish_email = serializers.CharField(max_length=32, source="publish.email") publish_name = serializers.CharField(max_length=32, source="publish.name") authors = serializers.SerializerMethodField() # 多对多字段 def get_authors(self, obj): # 这里的obj是循环中的书籍对象 ret = [] for author in obj.authors.all(): ret.append( {"name": author.name, "age": author.age} ) return ret
get请求数据结果:
[ { "id": 1, "publish_email": "132", "publish_name": "沙河出版社", "authors": [ { "name": "觉先生", "age": 18 }, { "name": "胡大炮", "age": 28 } ], "title": "西游记", "price": "123.00", "publishDate": null, "publish": 1 } ]
post提交数据时,数据库中没有的字段,就算是提交了,也不会报错,但是要是我们配置了某些字段,那么在提交时,这些字段也必须有,不然会校验失败;
如: 我们配置了 publish_email字段,所以提交数据,要求必须有这个字段,就算提交了这个字段,数据库也不会保存,但是不提交会校验失败。
注意:对于post请求的时候,如果我们有扩展字段,如上面我们写的publish,author等字段,我们在get()查的时候可以,但是在post请求提交数据时会报错!
看看源码:
def create(self, validated_data): raise_errors_on_nested_writes('create', self, validated_data) ModelClass = self.Meta.model # Remove many-to-many relationships from validated_data. # They are not valid arguments to the default `.create()` method, # as they require that the instance has already been saved. info = model_meta.get_field_info(ModelClass) # 就是我们post请求传过来的数据 many_to_many = {} for field_name, relation_info in info.relations.items(): # 循环post请求的数据 if relation_info.to_many and (field_name in validated_data): # 如果是多对多字段 many_to_many[field_name] = validated_data.pop(field_name) # 从validated_data中剔除,加到many_to_many字典中 try: instance = ModelClass.objects.create(**validated_data) # 此时的数据中已经没有多对多字段了,就可以直接create了 except TypeError: tb = traceback.format_exc() msg = ( 'Got a `TypeError` when calling `%s.objects.create()`. ' 'This may be because you have a writable field on the ' 'serializer class that is not a valid argument to ' '`%s.objects.create()`. You may need to make the field ' 'read-only, or override the %s.create() method to handle ' 'this correctly.\nOriginal exception was:\n %s' % ( ModelClass.__name__, ModelClass.__name__, self.__class__.__name__, tb ) ) raise TypeError(msg) # Save many-to-many relationships after the instance is created. if many_to_many: # 对于多对多字段, for field_name, value in many_to_many.items(): field = getattr(instance, field_name) # 相当于 book.authors.set() field.set(value) return instance
因为源码中,会直接拿着我们写的覆盖人家的那个字段(publish, )去create,所以只要我们覆盖了默认的字段,源码会在这里抛出错误!
这里就需要我们配置参数来解决这个问题了:
对于某些字段只可读,就设置read_only = True,某些字段不需要传过去,但是提交的时候需要,就设为write_only = true;
class BookSerializer(serializers.ModelSerializer): class Meta: model = models.Book # fields = ["title", "price", "publishDate"] # 可以指定字段 fields = "__all__" # 所有字段 extra_kwargs = {"publish": {"write_only": True}, "authors": {"write_only": True}} publish_email = serializers.CharField(max_length=32, source="publish.email", read_only=True) publish_name = serializers.CharField(max_length=32, source="publish.name", read_only=True) authors_info = serializers.SerializerMethodField() # 多对多字段 def get_authors_info(self, obj): # 这里的obj是循环中的书籍对象 ret = [] for author in obj.authors.all(): ret.append( {"name": author.name, "age": author.age} ) return ret
这样我们在get获取数据的时候,结果:
{ "id": 1, "publish_email": "132", "publish_name": "沙河出版社", "authors_info": [ { "name": "觉先生", "age": 18 }, { "name": "胡大炮", "age": 28 } ], "title": "西游记", "price": "123.00", "publishDate": null }
可以发现同时也没了默认的publish, authors字段,这是应为我们配置了:
extra_kwargs = {"publish": {"write_only": True}, "authors": {"write_only": True}}
让这两个字段只有在post提交的时候才生效,所以在post的时候这样传递数据也不会报错了!:
{ "title": "西游sdsdf记", "price": "123.00", "publishDate": "2018-03-25", "publish": 1, "authors": [1,5] }
六,put,delete,get/(\d+)请求
对于这三种请求,都需要一个参数,因为还有一个get的原因,我们不能这三个放在同一个视图中,所以需要再加一条路由线:
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r"^book/$", views.Book.as_view()), url(r"^book/(\d+)/$", views.SBook.as_view()), ]
他们的使用方式和modelform基本相同:
class SBook(APIView): def delete(self, request, pk): models.Book.objects.filter(pk=pk).delete() return Response("") def put(self, request, pk): obj = models.Book.objects.filter(pk=pk).first() bs = BookSerializer(instance=obj, data=request.data) # 注意instance if bs.is_valid(): bs.save() return Response(bs.data) else: return Response(bs.errors) def get(self, request, pk): obj = models.Book.objects.filter(pk=pk).first() bs = BookSerializer(obj, many=False) # 传入单个对象,返回的就是一个序列化后的字典 return Response(bs.data)
但是,这样的话,对于那种对数据进行增删该查的路由,就要每个表都这样写,每个表的代码又基本相同,重复代码这么多这不是扯蛋了么?
restframework早就帮我们做好了,去看看:restframework视图类


浙公网安备 33010602011771号