第三章 restframework——序列化组件
一、django自带的序列化组件serializers
二、restframework的序列化组件Serializer
三、restframework的序列化组件ModelSerializer
四、restframework的序列化组件超链接字段hypermedialink
五、restframework的序列化组件之请求数据校验和保存功能
六、补充内容
一、django自带的序列化组件serializers
先来看看django自带的cbv的json写法,然后我们再来看下restframework的写法
【django】序列化
class PublishView(View): def get(self,request): # 方式一 publish_list = list(Publish.objects.all().values('name','email')) return HttpResponse(json.dumps(publish_list)) # 方式二 from django.forms.models import model_to_dict publish_list = Publish.objects.all() temp = [] for obj in publish_list: temp.append(model_to_dict(obj)) return HttpResponse(json.dumps(temp)) # 方式三 from django.core import serializers publish_list = Publish.objects.all() temp = serializers.serialize("json",publish_list) return HttpResponse(temp)
使用步骤:
导入模块
from django.core import serializers
def persons(request):
ret = models.Person.objects.all()
# person_list = []
# for i in ret:
# person_list.append({"name":i.name,"age":i.age})
# print(person_list)
# import json
# s = json.dumps(person_list)
# print(s)
from django.core import serializers
s = serializers.serialize("json",ret)
print(s)
return HttpResponse(s)
二、restframework的序列化组件Serializer
准备工作
models.py
from django.db import models # Create your models here. class Book(models.Model): title=models.CharField(max_length=32) price=models.IntegerField() pub_date=models.DateField() publish=models.ForeignKey("Publish") authors=models.ManyToManyField("Author") def __str__(self): return self.title class Publish(models.Model): name=models.CharField(max_length=32) email=models.EmailField() def __str__(self): return self.name class Author(models.Model): name=models.CharField(max_length=32) age=models.IntegerField() def __str__(self): return self.name
【restframework】序列化
from django.shortcuts import * from django.views import View from .models import Publish from rest_framework.views import APIView import json # Create your views here. from rest_framework import serializers from rest_framework.response import Response # 为queryset,model对象做序列化 class PublishSerializers(serializers.Serializer): # 下面写的字段取决于你需要哪些字段做序列化 name = serializers.CharField() email = serializers.CharField() class PublishView(View): def get(self, request): publish_list = Publish.objects.all() #queryset对象 pb = PublishSerializers(publish_list, many=True) # many = true 代表前面传入的参数是queryset对象 print(pb.data) publish1 = Publish.objects.filter(pk=1).first() print(publish1) # publish对象 pb = PublishSerializers(publish1,many=False) # PublishSerializers(model_obj,many = false) # many = false 代表前面传入的参数是model对象 # 默认many = false,所以如果是model对象可以不用写many print(pb.data) return HttpResponse(pb.data)
使用步骤:
1.导入模块
from rest_framework import serializers
2.写需要序列化的类(一般以类名+Serializers命名)
# 为queryset,model对象做序列化 class PublishSerializers(serializers.Serializer): # 下面写的字段取决于你需要哪些字段做序列化 name = serializers.CharField() email = serializers.CharField() # publish=serializers.CharField(source="publish.name") # authors=serializers.CharField(source="authors.all")
注意:
source 如果是字段,会显示字段,如果是方法,会执行方法,不用加括号(authors=serializers.CharField(source='authors.all'))
如在模型中定义一个方法,直接可以在在source指定执行
3.在视图函数内调用序列化的类,使用restframework封装的Response类返回Response实例化对象
class PublishView(View): def get(self, request): publish_list = Publish.objects.all() #queryset对象 pb = PublishSerializers(publish_list, many=True) # many = true 代表前面传入的参数是queryset对象 print(pb.data) publish1 = Publish.objects.filter(pk=1).first() print(publish1) # publish对象 pb = PublishSerializers(publish1,many=False) # PublishSerializers(model_obj,many = false) # many = false 代表前面传入的参数是model对象 # 默认many = false,所以如果是model对象可以不用写many print(pb.data) return HttpResponse(pb.data)
注意:
many=true,代表前面传入的参数需是queryset对象,而many=false,代表前面传入的是publish对象,并非指的是1个queryset对象。
上面的演示是基于单表的简单操作,下面对Book表(存在多对多关系)进行演示
class BookSerializers(serializers.Serializer): title = serializers.CharField(max_length=32) price = serializers.IntegerField() pub_date = serializers.DateField() publish = serializers.CharField(source="publish.name") # authors=serializers.CharField(source="authors.all") #这样会返回queryset对象,看起来还不是特别舒服 authors = serializers.SerializerMethodField() # 这个方法只是为多对多服务的,下面要写类似钩子的方法 def get_authors(self,obj): temp = [] for obj in obj.authors.all(): temp.append(obj.name) return temp
class BookView(APIView): def get(self,request): book_list = Book.objects.all() bs = BookSerializers(book_list,many=True) ''' 序列化BookSerializers(book_list,many=True)做了如下操作: temp = [] for obj in book_list: temp.append({ "title":obj.title, "price":obj.price, "pub_date":obj.pub_date, "publish":obj.publish, # 这里的返回值就是上面get_authors的返回值 }) ''' return Response(bs.data) def post(self,request): pass
三、restframework的序列化组件ModelSerializer
大幅度简化Serializer操作
如果要指定一对多或多对多显示的传输字段可以参照上面重写get_方法名方法
class BookSerializers(serializers.ModelSerializer): class Meta: model = models.Book # fields = "__all__" fields=['nid','title','authors','publish'] # exclude=('nid',) #不能跟fields同时用 # depth = 1 #深度控制,写 几 往里拿几层,层数越多,响应越慢,官方建议0--10之间,个人建议最多3层 publish=serializers.SerializerMethodField() def get_publish(self,obj): return obj.publish.name authors=serializers.SerializerMethodField() def get_authors(self,obj): ret=obj.authors.all() ss=AuthorSerializer(ret,many=True) return ss.data
如果要指定一对多或多对对显示的传输字段需要重写create方法
多本书的相关操作涉及get和post,那么单本书的操作则涉及get,put,delete
让我们用ModelSerializer尝试做更多操作
【books】BookModelSerializer准备
class BookModelSerializers(serializers.ModelSerializer): class Meta: model = Book # 表名 fields = "__all__" # 默认把所有字段写入,不包括自己写的get_authors方法 # field = ['nid','title','authors','publish'] # 指定字段 # exclude = ('nid') # 除了...字段,不能跟fields同时用 # depth = 1 #深度控制,写 几 往里拿几层,层数越多,响应越慢,官方建议0--10之间,个人建议最多3层 # 如果要指定一对多,或多对多显示的字段需要重写create方法 publish = serializers.CharField(source='publish.pk') def create(self, validated_data): # print('validated_data',validated_data) book = Book.objects.create( title=validated_data['title'], price=validated_data['price'], pub_date=validated_data['pub_date'], publish_id=validated_data['publish']['pk'] ) book.authors.add(*validated_data['authors']) return book
books的【get】查看书籍
class BookView(APIView): def get(self,request): # get请求数据 book_list = Book.objects.all() bs = BookModelSerializers(book_list,many=True) ''' 序列化BookSerializers(book_list,many=True)做了如下操作: temp = [] for obj in book_list: temp.append({ "title":obj.title, "price":obj.price, "pub_date":obj.pub_date, "publish":obj.publish, # 这里的返回值就是上面get_authors的返回值 }) ''' return Response(bs.data)
books的【post】添加一本书籍(添加完之后可以再尝试用Postman发送get请求查看所有书籍信息)
class BookView(APIView): def get(self,request): ... def post(self,request): # post请求数据 bs = BookModelSerializers(data=request.data) if bs.is_valid(): print(bs.validated_data) bs.save() # create方法 return Response(bs.data) else: return HttpResponse(bs.errors)
【bookdetail】BookDetailModelSerializer准备
先处理url
bookdetail的【get】获取一本书
bookdetail的【put】修改一本书
bookdetail的【delete】获取一本书
四、restframework的序列化组件超链接字段hypermedialink
让我们再来写个publishdetail,你会发现里面的逻辑竟然和bookdetail一样,那restframework也有对应的方法可以简化
class BookSerializers(serializers.ModelSerializer): publish= serializers.HyperlinkedIdentityField( view_name='publish_detail', lookup_field="publish_id", lookup_url_kwarg="pk") class Meta: model=Book fields="__all__" #depth=1
res=BookSerializers(ret,many=True,context={'request': request})
五、restframework的序列化组件之请求数据校验和保存功能
class BookSerializers(serializers.ModelSerializer): class Meta: model=Book fields="__all__" #———————— class BookView(APIView): def post(self, request): # 添加一条数据 print(request.data) bs=BookSerializers(data=request.data) if bs.is_valid(): bs.save() # 生成记录 return Response(bs.data) else: return Response(bs.errors)
class BookSerializer1(serializers.Serializer): title=serializers.CharField(error_messages={'required': '标题不能为空'}) #这种方式要保存,必须重写create方法
通过源码查看留的校验字段的钩子函数:
#is_valid---->self.run_validation-(执行Serializer的run_validation)-->self.to_internal_value(data)---(执行Serializer的run_validation:485行) def validate_title(self, value): from rest_framework import exceptions raise exceptions.ValidationError('看你不顺眼') return value #全局 def validate(self, attrs): from rest_framework import exceptions if attrs.get('title')== attrs.get('title2'): return attrs else: raise exceptions.ValidationError('不想等啊')
附上代码调试用:
"""restdemo URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/1.11/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.conf.urls import url, include 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) """ from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^publishes/$',views.PublishView.as_view()), url(r'^publishes/(?P<pk>d+)/$',views.PublishDetailView.as_view(),name='publish_detail'), url(r'^books/$',views.BookView.as_view()), url(r'^books/(d+)/$',views.BookDetailView.as_view()) ]
from django.db import models # Create your models here. from django.db import models # Create your models here. class Book(models.Model): title=models.CharField(max_length=32) price=models.IntegerField() pub_date=models.DateField() publish=models.ForeignKey("Publish") authors=models.ManyToManyField("Author") def __str__(self): return self.title class Publish(models.Model): name=models.CharField(max_length=32) email=models.EmailField() # def __str__(self): # return self.name class Author(models.Model): name=models.CharField(max_length=32) age=models.IntegerField() def __str__(self): return self.name
from django.shortcuts import * from django.views import View from .models import * import json # Create your views here. from rest_framework.views import APIView from rest_framework.response import Response from .all_serializers import * # class BookSerializers(serializers.Serializer): # title = serializers.CharField(max_length=32) # price = serializers.IntegerField() # pub_date = serializers.DateField() # publish = serializers.CharField(source="publish.name") # # authors=serializers.CharField(source="authors.all") #这样会返回queryset对象,看起来还不是特别舒服 # authors = serializers.SerializerMethodField() # 这个方法只是为多对多服务的,下面要写类似钩子的方法 # def get_authors(self,obj): # temp = [] # for obj in obj.authors.all(): # temp.append(obj.name) # return temp class BookView(APIView): def get(self,request): # get请求数据 book_list = Book.objects.all() bs = BookModelSerializers(book_list,many=True) ''' 序列化BookSerializers(book_list,many=True)做了如下操作: temp = [] for obj in book_list: temp.append({ "title":obj.title, "price":obj.price, "pub_date":obj.pub_date, "publish":obj.publish, # 这里的返回值就是上面get_authors的返回值 }) ''' return Response(bs.data) def post(self,request): # post请求数据 bs = BookModelSerializers(data=request.data) if bs.is_valid(): print(bs.validated_data) bs.save() # create方法 return Response(bs.data) else: return HttpResponse(bs.errors) class BookDetailView(APIView): def get(self,request,id): book = Book.objects.filter(pk=id).first() bs = BookModelSerializers(book) return Response(bs.data) def put(self,request,id): book = Book.objects.filter(pk=id).first() # 因为是做更新操作,所以要写data=request.data,把新数据放进去 bs = BookModelSerializers(book,data=request.data) if bs.is_valid(): bs.save() return Response(bs.data) else: return Response(bs.errors) def delete(self,request,id): Book.objects.filter(pk=id).delete() # 删除操作返回空即可 return Response() class PublishView(APIView): def get(self, request): publish_list = Publish.objects.all() ps = PublishModelSerializers(publish_list, many=True) return Response(ps.data) # 取数据 # print('restframework', request.data) # print('restframework type', type(request.data)) # print('restframework', request._request.GET) # print('restframework type', type(request._request.GET)) # print('restframework', request.GET) # print('restframework type', type(request.GET)) # return HttpResponse('ok') # 序列化 # publish_list = Publish.objects.all() #queryset对象 # pb = PublishSerializers(publish_list, many=True) # many = true 代表前面传入的参数是queryset对象 # print(pb.data) # publish1 = Publish.objects.filter(pk=1).first() # print(publish1) # publish对象 # pb = PublishSerializers(publish1,many=False) # PublishSerializers(model_obj,many = false) # many = false 代表前面传入的参数是model对象 # 默认many = false,所以如果是model对象可以不用写many # print(pb.data) # return HttpResponse(pb.data) # 方式一 # publish_list = list(Publish.objects.all().values('name','email')) # return HttpResponse(json.dumps(publish_list)) # 方式二 # from django.forms.models import model_to_dict # publish_list = Publish.objects.all() # temp = [] # for obj in publish_list: # temp.append(model_to_dict(obj)) # return HttpResponse(json.dumps(temp)) # 方式三 # from django.core import serializers # publish_list = Publish.objects.all() # temp = serializers.serialize("json",publish_list) # return HttpResponse(temp) def post(self, request): # 取数据 # 原生的request操作 # print('POST',request.POST) # print('body',request.body) # print(type(request)) # from django.core.handlers.wsgi import WSGIRequest # print(request.data) # 新的request支持的操作 # 要想使用之前request的方法 # ret = request._request.POST # django将request的所有方法的数据获取进行了不同的封装 # 而restframework将request下POST方法的数据封装成request.data # print('restframework',request.data) # print('restframework type',type(request.data)) # return HttpResponse('post') ps = PublishModelSerializers(data=request.data) if ps.is_valid(): print(ps.validated_data) ps.save() # create方法 return Response(ps.data) else: return Response(ps.errors) class PublishDetailView(APIView): def get(self, request, pk): publish = Publish.objects.filter(pk=pk).first() ps = PublishModelSerializers(publish) return Response(ps.data) def put(self, request, pk): publish = Publish.objects.filter(pk=pk).first() ps = PublishModelSerializers(publish, data=request.data) if ps.is_valid(): ps.save() return Response(ps.data) else: return Response(ps.errors) def delete(self, request, pk): Publish.objects.filter(pk=pk).delete() return Response()
from rest_framework import serializers from .models import * # 为queryset,model对象做序列化 # class PublishSerializers(serializers.Serializer): # # 下面写的字段取决于你需要哪些字段做序列化 # name = serializers.CharField() # email = serializers.CharField() class PublishModelSerializers(serializers.ModelSerializer): class Meta: model=Publish fields="__all__" class BookModelSerializers(serializers.ModelSerializer): class Meta: model = Book # 表名 fields = "__all__" # 默认把所有字段写入,不包括自己写的get_authors方法 # field = ['nid','title','authors','publish'] # 指定字段 # exclude = ('nid') # 除了...字段,不能跟fields同时用 # depth = 1 #深度控制,写 几 往里拿几层,层数越多,响应越慢,官方建议0--10之间,个人建议最多3层 # 如果要指定一对多,或多对多显示的字段需要重写create方法 publish = serializers.CharField(source='publish.pk') authors = serializers.CharField(source="authors.all") def create(self, validated_data): # print('validated_data',validated_data) book = Book.objects.create( title=validated_data['title'], price=validated_data['price'], pub_date=validated_data['pub_date'], publish_id=validated_data['publish']['pk'] ) book.authors.add(*validated_data['authors']) return book
六、补充内容
【get_xxx_display()方法】
models.py
ser.py
这是个固定用法,打印看下结果
【小技巧】
可以使用https://www.json.cn来查看格式化的json