DjangoRestFramework学习二之序列化组件
本节目录html
首先按照restful规范我们建立一些api接口,按照下面这些形式写吧:前端
Courses --- GET ---> 查看数据----->返回全部数据列表[{},{},]java
Courses--- POST --->添加数据 -----> 返回添加的数据{ }python
courses/1 ---PUT---> 更新pk=1的数据 ----->返回更新后的数据{ }linux
courses/1 --- DELETE---> 删除pk=1的数据 -----> 返回空git
courses/1 --- GET --->查看单条数据 -----> 返回单条数据 { }django
这样,咱们先看一个drf给咱们提供的一个相似于Postman功能的页面,首先咱们建立一个django项目,建立一个Course表,而后添加一些数据,而后按照下面的步骤操做,json
第一步:引入drf的Response对象 api
from django.shortcuts import render,HttpResponse,redirect import json from django.views import View from app01 import models from rest_framework.views import APIView #引用drf提供的Response对象 from rest_framework.response import Response
#写咱们的CBV视图 class CourseView(APIView): #返回全部的Course数据 def get(self,request): course_obj_list = models.Course.objects.all() ret = [] for course_obj in course_obj_list: ret.append({ "title":course_obj.title, "desc":course_obj.desc, }) # return HttpResponse(json.dumps(ret, ensure_ascii=False)) return Response(json.dumps(ret, ensure_ascii=False)) #这里使用Response来返回消息 def post(self,request): print(request.data) return HttpResponse('POST')
第二步:配置App,在咱们的settings配置文件中配置浏览器
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01.apps.App01Config', 'rest_framework', #将它注册成App ]
第三步,配置咱们的路由
""" from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^courses/', views.CourseView.as_view(),name='courses'), ]
第四步:启动项目,经过浏览器访问咱们的路由(必须是浏览器访问才能看到对应的功能),看效果:
这里面咱们能够发送不一样类型的请求,看到对应的返回数据,相似于Postman,可是没有Postman好用,因此之后调试咱们仍是用Postman工具,可是咱们知道一下昂。
上面的数据,咱们经过json本身进行的序列化,其实django也给咱们提供了一个简单的序列化组件,看用法:
from django.shortcuts import render,HttpResponse,redirect import json from django.views import View from app01 import models from rest_framework.views import APIView from django.core.serializers import serialize #django的序列化组件,不是咱们要学的drf的序列化组件昂 #不用json本身来序列化了,太麻烦,咱们使用drf提供的序列化组件 from rest_framework.response import Response class CourseView(APIView): def get(self,request): course_obj_list = models.Course.objects.all() # ret = [] # for course_obj in course_obj_list: # ret.append({ # "title":course_obj.title, # "desc":course_obj.desc, # }) # return HttpResponse(json.dumps(ret, ensure_ascii=False)) # return Response(json.dumps(ret, ensure_ascii=False) se_data = serialize('json',course_obj_list,ensure_ascii=False) print(se_data)#也拿到了序列化以后的数据,简洁不少 #[{"model": "app01.course", "pk": 1, "fields": {"title": "python", "desc": "666"}}, {"model": "app01.course", "pk": 2, "fields": {"title": "linux", "desc": "\u4e5f\u5f88\u597d"}}, {"model": "app01.course", "pk": 3, "fields": {"title": "go", "desc": "\u5c06\u6765\u53ef\u80fd\u5f88\u597d"}}] return Response(se_data)
那么咱们知道了两个序列化方式了,这个序列化是否是就简单不少啊,可是drf给咱们作了一个更牛逼的序列化组件,功能更强大,并且不只仅能作序列化,还能作其余的事情,因此呢,作api的时候,咱们仍是用drf提供的序列化组件。

import json from datetime import datetime from datetime import date #对含有日期格式数据的json数据进行转换 class JsonCustomEncoder(json.JSONEncoder): def default(self, field): if isinstance(field,datetime): return field.strftime('%Y-%m-%d %H:%M:%S') elif isinstance(field,date): return field.strftime('%Y-%m-%d') else: return json.JSONEncoder.default(self,field) d1 = datetime.now() dd = json.dumps(d1,cls=JsonCustomEncoder) print(dd)
接下来重点到了,咱们玩一下drf提供的数据序列化组件:
1.咱们经过GET方法,来查看全部的Course数据。
from django.shortcuts import render,HttpResponse,redirect import json from django.views import View from app01 import models from rest_framework.views import APIView from django.core.serializers import serialize #django的序列化组件,不是咱们要学的drf的序列化组件昂 from rest_framework.response import Response # 序列化方式3,1.引入drf序列化组件 from rest_framework import serializers # 2.首先实例化一个类,继承drf的serializers.Serializer,相似于咱们的form组件和models的用法 class CourseSerializers(serializers.Serializer): #这里面也要写对应的字段,你写了哪些字段,就会对哪些字段的数据进行序列化,没有被序列化的字段,不会有返回数据,你能够注释掉一个,而后看返回的数据是啥 title = serializers.CharField(max_length=32) #序列化的时候还能校验字段 desc = serializers.CharField(max_length=32) class CourseView(APIView): def get(self,request): course_obj_list = models.Course.objects.all() # 3.使用咱们建立的序列化类 cs = CourseSerializers(course_obj_list, many=True) # 序列化多个对象的时候,须要些many=True参数 #4.经过返回对象的data属性就能拿到序列化以后的数据 se_data = cs.data print(se_data) #[OrderedDict([('title', 'python'), ('desc', '666')]), OrderedDict([('title', 'linux'), ('desc', '也很好')]), OrderedDict([('title', 'go'), ('desc', '未来可能很好')])] 列表嵌套的有序字典。 #还记得建立字典的另一种写法吗?这个没啥用昂,给你们回顾一下以前的知识 # d1 = {'name':'chao'} # d2 = dict([('name','chao'),('age',18)]) # print(d1) #{'name': 'chao'} # print(d2) #{'age': 18, 'name': 'chao'} # # 有序字典 # from collections import OrderedDict # d3 = OrderedDict([('name','Jaden'),('age',22)]) # print(d3) #OrderedDict([('name', 'Jaden'), ('age', 22)]) return Response(se_data) #drf的Response若是返回的是drf序列化以后的数据,那么客户端拿到的是一个有格式的数据,再也不是一行显示了
看效果:
2.经过POST方法来添加一条数据:
from django.shortcuts import render,HttpResponse,redirect from django.views import View from app01 import models from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import serializers class CourseSerializers(serializers.Serializer): title = serializers.CharField(max_length=32) desc = serializers.CharField(max_length=32) class CourseView(APIView): def get(self,request): course_obj_list = models.Course.objects.all() cs = CourseSerializers(course_obj_list, many=True) se_data = cs.data return Response(se_data) def post(self,request): # print(request.data) #{'desc': 'java也挺好', 'title': 'java'} #发送过来的数据是否是要进行验证啊,drf的序列化组件还能校验数据 cs = CourseSerializers(data=request.data,many=False) #注意必须是data=这种关键字参数,注意,验证单条数据的时候写上many=False参数,并且咱们还要序列化这个数据,由于咱们要给客户端返回这个数据 # print(cs.is_valid()) #True ,若是少数据,获得的是False if cs.is_valid(): print(cs.data) models.Course.objects.create(**cs.data)#添加数据 return Response(cs.data) #按照post添加数据的api规则,我们要返回正确的数据 else: # 假如客户端发送过来的数据是这样的,少title的数据 # { # "desc": "java也挺好" # } cs_errors = cs.errors # print(cs_errors) #{'title': ['This field is required.']} return Response(cs_errors) # postman上咱们看到的效果是下面这样的 # { # "title": [ # "This field is required." # ] # }
而后添加一些数据,好,接下来咱们玩一些有关联关系的表
class Author(models.Model): nid = models.AutoField(primary_key=True) name=models.CharField( max_length=32) age=models.IntegerField() class AuthorDetail(models.Model): nid = models.AutoField(primary_key=True) birthday=models.DateField() telephone=models.BigIntegerField() addr=models.CharField( max_length=64) class Publish(models.Model): nid = models.AutoField(primary_key=True) name=models.CharField( max_length=32) city=models.CharField( max_length=32) email=models.EmailField() class Book(models.Model): nid = models.AutoField(primary_key=True) title = models.CharField( max_length=32) publishDate=models.DateField() price=models.DecimalField(max_digits=5,decimal_places=2) publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE) #多对一到Publish表 authors=models.ManyToManyField(to='Author',) #多对多到Author表
看序列化代码:
from django.shortcuts import render,HttpResponse,redirect from django.views import View from app01 import models from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import serializers class BookSerializers(serializers.Serializer): #咱们先序列化写两个字段的数据,别忘了这里面的字段和model表中的字段变量名要同样 title = serializers.CharField(max_length=32) price = serializers.DecimalField(max_digits=5, decimal_places=2) #一对多的处理 # publish = serializers.CharField(max_length=32) #返回对象 publish_email = serializers.CharField(max_length=32, source='publish.email') # source指定返回的多对一的那个publish对象的email数据,而且咱们如今找到书籍的email,因此前面的字段名称就能够不和你的publish对应好了,随便取名字 publish_name = serializers.CharField(max_length=32, source='publish.email') # source指定返回的多对一的那个publish对象的其余字段数据,能够接着写字段,也就是说关联的全部的字段的数据均可以写在这里进行序列化 #对多对的处理 # authors = serializers.CharField(max_length=32) #bookobj.authors拿到的相似于一个models.Authors.object,打印的时候这是个None # authors = serializers.CharField(max_length=32,source="authors.all") #这样写返回的是queryset类型的数据,这样给前端确定是不行的,因此按照下面的方法写 authors = serializers.SerializerMethodField() #序列化方法字段,专门给多对多字段用的,而后下面定义一个方法,方法名称写法是这样的get_字段名,名字必须是这样 def get_authors(self,obj): #参数写一个obj,这个obj是一个一个的书籍对象,而后咱们经过书籍对象来返回对应的数据 # author_list_values = obj.authors.all().values() #返回这样类型的数据也行,那么具体你要返回什么结构的数据,须要和前端人员沟通清楚,而后这里对数据进行加工 #假如加工成的数据是这种类型的[ {},{} ],就能够按照下面的逻辑来写,我简单写的,确定有更好的逻辑来加工这些数据 author_list_values = [] author_dict = {} author_list = obj.authors.all() for i in author_list: author_dict['name'] = i.name author_list_values.append(author_dict) return author_list_values class BookView(APIView): def get(self,request): book_obj_list = models.Book.objects.all() s_books = BookSerializers(book_obj_list,many=True) return Response(s_books.data) def post(self,request): pass
其实serializer在内部就作了这点事儿,伪代码昂。
urls.py是这样写的:
urlpatterns = [ #url(r'^admin/', admin.site.urls), #作一些针对书籍表的接口 url(r'^books/', views.BookView.as_view(),), ]
而后看Postman返回的数据:
那么咱们就可以完成各类数据的序列化了,可是你会发现,这样写太累啦,这只是一张表啊,要是上百张表咋整啊,因此还有一个更简单的方式(相似于form和modelform的区别)。
咱们使用ModelSerializer,看代码:
#ModelSerializer class BookSerializers(serializers.ModelSerializer): class Meta: model=models.Book # fields=['title','price','publish','authors'] fields = "__all__" # 若是直接写all,你拿到的数据是下面这样的,可是若是人家前端和你要的做者的id和名字,你是否是要处理一下啦 # [ # { # "nid": 3, # "title": "go", # "publishDate": null, # "price": "122.00", # "publish": 2, # "authors": [ # 2, # 1 # ] # } # ] #那么没办法,只能本身再进行加工处理了,按照以前的方式 authors = serializers.SerializerMethodField() def get_authors(self,obj): author_list_values = [] author_dict = {} author_list = obj.authors.all() for i in author_list: author_dict['id'] = i.pk author_dict['name'] = i.name author_list_values.append(author_dict) return author_list_values #这个数据就会覆盖上面的序列化的authors字段的数据 # 那么前端拿到的数据就这样了 # [ # { # "nid": 3, # "authors": [ # { # "name": "chao", # "id": 1 # }, # { # "name": "chao", # "id": 1 # } # ], # "title": "go", # "publishDate": null, # "price": "122.00", # "publish": 2 # } # ] # 那若是一对多关系的那个publish,前端想要的数据是名字怎么办呢?仍是老办法,source # publish_name = serializers.CharField(max_length=32, source='publish.name')#可是你会发现序列化以后的数据有个publish:1对应个id值,若是我不想要他怎么办,那么能够起个相同的变量名来覆盖它,好比下面的写法 publish = serializers.CharField(max_length=32, source='publish.name') class BookView(APIView): def get(self,request): book_obj_list = models.Book.objects.all() s_books = BookSerializers(book_obj_list,many=True) return Response(s_books.data) def post(self,request): pass
上面咱们完成了get请求来查看全部的书籍信息,接下来咱们玩一个post请求添加一条book数据,直接上代码吧:
class BookSerializers(serializers.ModelSerializer): class Meta: model=models.Book fields = "__all__" # 注意先把下面这些注释掉 # authors = serializers.SerializerMethodField() # def get_authors(self,obj): # author_list_values = [] # author_dict = {} # author_list = obj.authors.all() # for i in author_list: # author_dict['id'] = i.pk # author_dict['name'] = i.name # author_list_values.append(author_dict) # return author_list_values # publish = serializers.CharField(max_length=32, source='publish.name') class BookView(APIView): def get(self,request): book_obj_list = models.Book.objects.all() s_books = BookSerializers(book_obj_list,many=True) return Response(s_books.data) def post(self,request): b_serializer = BookSerializers(data=request.data,many=False) if b_serializer.is_valid(): print('xxxx') b_serializer.save() #由于这个序列化器咱们用的ModelSerializer,而且在BookSerializers类中咱们指定了序列化的哪一个表,因此直接save,它就知道咱们要将数据保存到哪张表中,其实这句话执行的就是个create操做。 return Response(b_serializer.data) #b_serializer.data这就是个字典数据 else: return Response(b_serializer.errors)
上面咱们完成了GET和POST请求的接口写法,下面咱们来完成PUT、DELETE、GET查看单条数据的几个接口。
urls.py内容以下:
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), # url(r'^courses/', views.CourseView.as_view()), #作一些针对书籍表的接口 #GET和POST接口的url url(r'^books/$', views.BookView.as_view(),), #别忘了$符号结尾 #PUT、DELETE、GET请求接口 url(r'^books/(\d+)/', views.SBookView.as_view(),), ]
views.py代码以下:
from django.shortcuts import render,HttpResponse,redirect from django.views import View from app01 import models from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import serializers class BookSerializers(serializers.ModelSerializer): class Meta: model=models.Book fields = "__all__" class BookView(APIView): def get(self,request): ''' 查看全部书籍 :param request: :return: ''' book_obj_list = models.Book.objects.all() s_books = BookSerializers(book_obj_list,many=True) return Response(s_books.data) def post(self,request): ''' 添加一条数据 :param request: :return: ''' b_serializer = BookSerializers(data=request.data,many=False) if b_serializer.is_valid(): b_serializer.save() return Response(b_serializer.data) else: return Response(b_serializer.errors) #由于更新一条数据,删除一条数据,获取一条数据,都有个单独的参数(获取一条数据的,通常是id,因此我将put、delete、get写到了一个视图类里面,也就是说结合上面那个BookView视图类,完成了咱们的那些接口) class SBookView(APIView): def get(self,request,id): ''' 获取单条数据 :param request: :param id: :return: ''' book_obj = models.Book.objects.get(pk=id)#获取这条数据对象 #接下来序列化单个model对象,序列化单个对象返回的是一个字典结构 {},序列化多个对象返回的是[{},{}]这种结构 book_serializer = BookSerializers(book_obj,many=False) return Response(book_serializer.data) def put(self,request,id): ''' 更新一条数据 :param request:request.data更新提交过来的数据 :param id: :return: ''' book_obj = models.Book.objects.get(pk=id) b_s = BookSerializers(data=request.data,instance=book_obj,many=False) #别忘了写instance,因为咱们使用的ModelSerializer,因此前端提交过来的数据必须是全部字段的数据,固然id字段不用 if b_s.is_valid(): b_s.save() #翻译成的就是update操做 return Response(b_s.data) #接口规范要求我们要返回更新后的数据 else: return Response(b_s.errors) def delete(self,request,id): ''' 删除一条数据 :param request: :param id: :return: ''' book_obj = models.Book.objects.get(pk=id).delete() return Response("") #别忘了接口规范说最好返回一个空
好,五个接口写完,我们的序列化组件就算是讲完了。