DRF序列化组件
目录
--- 基于GenericAPIView类写views.py中的五个接口函数
--- 基于GenericAPIView类和五个扩展类写views.py中的五个接口函数
--- 基于GenericAPIView类的9个子类写views.py中的五个接口函数
--- 基于ModelViewSet类写views.py中的五个接口函数
-DRF
--定义
DRF(Django REST Framework)是一个建立在Django基础之上的Web应用开发框架,可以快速的开发REST API接口应用。
在REST Framework中,提供了序列化器Serializer的定义,可以帮助我们简化序列化和反序列化的过程;
不仅如此,还提供丰富的类视图、扩展类、视图集来简化视图的编写工作。
同时,REST Framework还提供了认证、权限、限流、过滤、分页、接口文档等功能支持;
还提供了一个API的Web可视化界面来方便查看测试接口
核心功能:缩减编写api接口的代码
--安装
--基本使用
drf使用的都是CBV的方式
1、在settings.py中注册 INSTALLEN_APPS = [ 'rest_framework', # 固定注册方式 ] 2、在models.py中书写模型表 class Book(models.Model): id = models.AutoField(primary_key = True) name = models.CharField(max_length=32) price = models.DecimalField(max_digits=5, decimal_places=2) author = models.CharField(max_length=16) 执行数据库迁移命令 3、基于CBV方式写视图函数 from rest_framework.views import APIView class BookApiView(APIView): def get(self, request,*args,**kwargs): #可接收多参数,下同 return HttpResponse('get请求') def post(self, request): # print(request.data) # 当post请求过来时,不管什么格式的数据(json,urlencoded)都可在request.data中接收 return HttpResponse('post请求') … 4、注册路由 path('/books', views.BookApiView.as_view()), # 要加括号!
然后在postman上就可以通过http://127.0.0.1:8000/books/通过请求方式的不同调用不同的视图函数
path('books/', views.BookApiView.as_view()), def get(self, request, pk=None): # 或用**kwargs判断 # 序列化 先获得待查询pk的queryset对象 if pk is None: book_obj = models.Book.objects.all() # BookSerializers实例化生成序列化对象 book_serializers_obj = BookSerializers(book_obj, many=True)
--drf中的request
- request.data """ 新的request有一个新的属性:data - data是post请求携带的数据,返回结果为字典的形式 - 在django中我们知道不同的编码格式存储在不同的方法中,如request.POST获取urlencoded编码格式的数据;request.body获取json格式的数据 - 而在drf中,无论什么编码格式,只要是post提交的数据,都在request.data中 所以以后取post提交的数据不要从request.POST中去了,要从request.data中取 """ - request.query_params # 就是request._request.GET """取get请求的数据不要从request.GET中取了,而是从request.query_params中取""" - request.FIELS '''上传的文件'''
request对象: from rest_framework.request import Request 点击Request查看源码: def __init__(self, request, parsers=None, authenticators=none,negotiator=None, parser_context=None): # 二次封装django的request对象,将其作为drf的_request属性 self._request = request … … response对象: from rest_framework.response import Response 点击Response查看源码: def __init__(self, data=None,status=None,template_name=None, headers=None,exception=None, content_type=None): … data:要返回的数据(字典) status:返回状态码,默认200 from rest_framework import status 里面定义了很多状态码常量 eg:return Response(data,status=status.HTTP_200_OK)
-序列化组件
--基本介绍
DRF自带一个序列化组件Serializer(类)
作用:
1.序列化:Serializer会把模型对象(表对象,QuerySet对象)转换成字典,经过response后变为json字符串
2.反序列化:把客户端发来的数据经过request后变为字典(request.data),Serializer可以把字典转为模型对象
3.完成数据校验功能
--基本使用
---①Serializer
1、新建一个.py序列化组件文件(见名知意),在该文件下写序列化类,继承Serializer from rest_framework import serializers class BookSerializers(serializers.Serializer): ... 2、在类中写要序列化的字段 # 写要序列化的字段 id = serializers.CharField() name = serializers.CharField(max_length=16,min_length=4) # book_name = serializers.CharField(max_length=16,min_length=4, source='get_book_name') price = serializers.CharField() author = serializers.CharField() 3、写路径(这以查单个为例,发get请求,带主键为筛选条件) re_path('books/(?P<pk>\d+)/', views.BookApiView.as_view()) 4、在视图类中使用 from app01.mySerializer1 import BookSerializers from rest_framework.response import Response class BookApiView(APIView): def get(self, request, pk): # 序列化 先获得待查询pk的queryset对象 book_obj = models.Book.objects.filter(pk=pk).first() # BookSerializers实例化生成序列化对象 book_serializers_obj = BookSerializers(book_obj) # 固定返回格式 book_serializers_obj.data是一个字典 return Response(book_serializers_obj.data) """ Response是drf提供的响应对象,如果不用Response,就用JsonResponse,不过就没有drf提供的那么多功能了(比如浏览器查返回好看界面) """
---②ModelSerializer
'''序列化类也可以不继承Serializer,而是继承ModelSerializer(模型序列化组件),在Meta中对应模型表及规定序列化字段''' from rest_framework.serializers import ModelSerializer from app01.models import Book class BookModelSerializer(ModelSerializer): # 类名任取 class Meta: model = Book # 对应models.py中的模型表 fields = '__all__' # 序列化所有字段 # fields = ('name', 'price') # 序列化指定字段 列表or元组 # exclude = ('name', ) # 除外均序列化 列表or元组 # read_only_fields = ['id'] # 设置只读字段 '''extra_kwargs = [ # 设置其它 'price' : {'write_only' : True}, ]''' # depth = 1 # 深度:深一层(外键深入的意思,=1表示把关联了一层的外键字段也显示出来) 非常耗费效率,不能控制深入后想要查询的字段,不建议使用,写多层不会报错,有几层就深入几层 视图函数中使用方法一样,只是不需要重写create和update方法了。其余使用相同
---序列化组件常用参数
----常用字段类型
----常用选项参数
----通用参数
read_only 默认false,表明该字段仅读仅用于序列化输出。为true时postman中可以看到该字段,修改时不需要传该字段
write_only 默认false,表明该字段仅用于反序列化输出。为true时postman看不到该字段,修改时该字段需要传
required 表明该字段在反序列化时必须输入,默认true
default 反序列化时使用的默认值
allow_null 默认false,是否允许传入None
validators 使用的验证器
error_messages 包含错误代码和错误信息的字典
---序列化组件修改数据
当发送PUT请求时,修改数据
def put(self, request, pk): back_dict = {'code':1000, 'msg':'成功'} book_obj = models.Book.objects.filter(pk=pk).first() # 实例化生成对象时传入修改数据 # book_serializers_obj = BookSerializers(book_obj, request.data) book_serializers_obj = BookSerializers(instance=book_obj, data=request.data) # 修改数据 验证修改后的数据是否合适 合适才能修改 if book_serializers_obj.is_valid(): # 保存数据 book_serializers_obj.save() # 会报错 需重写updata()方法 back_dict['datas'] = book_serializers_obj.data else: back_dict['code'] = 1001 back_dict['msg'] = '失败' back_dict['error'] = book_serializers_obj.errors return Response(back_dict)
当执行book_serializers_obj.save()保存修改后的数据时,会报错:
说update()方法必须重写,点击save()查看update()源码得:
def update(self, instance, validated_data): raise NotImplementedError('`update()` must be implemented')
原来是作者定义时自动把update()写为raise异常
所以我们想要成功修改数据就必须重写update()方法(在定义的序列化类里书写)
# 重写update方法 def update(self, instance, validated_data): """ :param instance: 就是当前的queryset对象 :param validated_data: 校验成功的数据 """ instance.id = validated_data.get('id') instance.name = validated_data.get('name') instance.price = validated_data.get('price') instance.author = validated_data.get('author') instance.save() return instance
之后再发送PUT请求就能修改数据成功了
---序列化组件删除数据
当发送DELETE请求时,删除数据
def delete(self, request, pk): # 删除数据无需序列化和反序列化,直接删除即可 models.Book.objects.filter(pk=pk).delete() # 删除成功返回一个空,如果没删除成功会自动抛异常返回一个空 return Response()
---序列化组件新增数据
当发送post请求时,可以新增数据
def post(self, request): back_dict = {'code': 1000, 'msg': '成功'} book_serializers_obj = BookSerializers(data=request.data) # 如果要新增就不需要传instance,按关键字传 if book_serializers_obj.is_valid(): book_serializers_obj.save() back_dict['data'] = book_serializers_obj.data else: back_dict['code'] = 1001 back_dict['msg'] = '失败' back_dict['error'] = book_serializers_obj.errors return Response(back_dict)
同理,新增数据需要重写create()方法(在定义的序列化类中书写)
def create(self, validated_data): # validated_data为用户上传的数据 # 通过表对象保存,这样就知道了保存到那张表中 instance = models.Book.objects.create(**validated_data) # 打散保存 # 返回 return instance
每次与前端交互时都需定义一个back_dict字典,太麻烦 可自己新建一个.py文件封装一个对象,实现传字典的功能 class MyResponse(): def __init__(self): self.code = 1000 self.msg = '成功' @property def get_dict(self): return self._ _dict_ _ 此时: # 发送post请求 新增数据 def post(self, request, *args, **kwargs): response_obj = MyResponse() book_serializers_obj = BookSerializers(data=request.data) # 如果要新增就不需要传instance,按关键字传 if book_serializers_obj.is_valid(): book_serializers_obj.save() response_obj.data = book_serializers_obj.data else: response_obj.code = 1001 response_obj.msg = '失败' response_obj.error = book_serializers_obj.errors return Response(response_obj.get_dict)
---source使用
在序列化组件时,序列化类中的字段必须与模型表内的字段是对应的
如果序列化时不对应,请求来访问时就会报错。如果想使用别的字段名①,就可以用source
book_name = serializers.CharField(source='name') # source这可以把它看做Book对象(instance),source='name'就<==>instance.name获取book对象的属性名name传给book_name
source也可以对应模型表中的方法②(方法返回结果是什么,字段对应结果就是什么)
models.py class Book(models.Model): ... def get_book_name(self): return '四大名著:' + self.name 序列化类 class BookSerializers(serializers.Serializer): book_name = serializers.CharField(source='get_book_name')
当该模型表下还有与其他表相关联的外键字段时,source还支持跨表查询③以使外键字段显示相应数据 主要用来做一对多,多对多的字段返回
models.py class Book(models.Model): ... # 与出版社关联 publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE, null=True) 序列化类 class BookSerializers(serializers.Serializer): ... publish_addr = serializers.CharField(source='publish.address')
--- SerializerMethodField使用
SerializerMethodField能帮助拿到关联表所有字段的值(如拿到出版社的详细信息)
使用SerializerMethodField就必须指定一个get_字段名的方法,该字段名是SerializerMethodField方法需要序列化的字段
序列化类中 class BookSerializers(serializers.Serializer): 。。。 # SerializerMethodField使用 必须搭配一个函数:get_字段名 publish = serializers.SerializerMethodField() def get_publish(self, instance): # instance即为book对象 publish_obj = instance.publish # 跨表到publish对象 datas_list = [{'name':publish_obj.name,'addr':publish_obj.address}] return datas_list
--- 局部钩子和全局钩子
class BookSerializers(serializers.Serializer): 。。。 # 局部钩子 validate_字段名 data为字段对应数据(名字任取) def validate_price(self, data): if float(data) >= 0: return data else: raise ValidationError('价格非负') # 全局钩子 attrs为全部合格数据(名字任取) def validate(self, attrs): name = attrs.get('name') author = attrs.get('author') if name == author: raise ValidationError('书籍名和作者名字相同') else: return attrs
--进阶使用
--- 基于GenericAPIView类写views.py中的五个接口函数
效果等价,只是比基于APIView写的代码少。∵都继承APIView,但有了更多方法
models.py class Publish(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=32) address = models.CharField(max_length=64) def __str__(self): return self.name urls.py # 基于GenericAPIView的关于Publish path('publishes/', views.PublishGenericAPIView.as_view()), re_path('publishes/(?P<pk>\d+)/', views.PublishGenericAPIViewByPK.as_view()), views.py from rest_framework.generics import GenericAPIView class PublishGenericAPIView(GenericAPIView): queryset = models.Publish.objects #queryset = models.Publish.objects跟上面等价的 serializer_class = PublishSerializers # 发送get请求查所有 def get(self, request): publish_obj = self.get_queryset() publish_serializers_obj = self.get_serializer(publish_obj,many=True) return Response(publish_serializers_obj.data) # 发送post请求 新增数据 def post(self, request): publish_serializers_obj = self.get_serializer(data=request.data) if publish_serializers_obj.is_valid(): publish_serializers_obj.save() # 需重写create方法吗?——要 return Response(publish_serializers_obj.data) else: return Response({'msg':'数据不合要求'}) class PublishGenericAPIViewByPK(GenericAPIView): queryset = models.Publish.objects serializer_class = PublishSerializers # get请求查单个数据 def get(self, request, pk): publish_obj = self.get_object() publish_serializers_obj = self.get_serializer(publish_obj) return Response(publish_serializers_obj.data) # PUT请求修改数据 def put(self, request, pk): publish_obj = self.get_object() publish_serializers_obj = self.get_serializer(publish_obj,data=request.data) if publish_serializers_obj.is_valid(): publish_serializers_obj.save() # 需重写updata()方法吗?——要 return Response(publish_serializers_obj.data) else: return Response({'msg':'修改后的数据不合格'}) # 发送delete请求 删除单个 def delete(self, request, pk): self.get_object().delete() return Response({'msg':'删除成功'})
--- 基于GenericAPIView类和五个扩展类写views.py中的五个接口函数
urls.py path('publishes/', views.PublishGenericAPIView5others.as_view()), re_path('publishes/(?P<pk>\d+)/', views.PublishGenericAPIView5othersByPK.as_view()), views.py from rest_framework.mixins import ListModelMixin,CreateModelMixin,\ UpdateModelMixin,RetrieveModelMixin,DestroyModelMixin class PublishGenericAPIView5others(GenericAPIView,ListModelMixin,CreateModelMixin): queryset = models.Publish.objects serializer_class = PublishSerializers def get(self, request): return self.list(request) # 查所有 def post(self, request): return self.create(request) # 增 class PublishGenericAPIView5othersByPK(GenericAPIView,UpdateModelMixin,RetrieveModelMixin,DestroyModelMixin): queryset = models.Publish.objects serializer_class = PublishSerializers def get(self, request, pk): return self.retrieve(request,pk) # 查单个 def put(self, request, pk): return self.update(request,pk) # 改 def delete(self, request, pk): return self.destroy(request,pk) # 删
--- 基于GenericAPIView类的9个子类写views.py中的五个接口函数
urls.py path('publishes/', views.PublishGenericAPIView9children.as_view()), re_path('publishes/(?P<pk>\d+)/', views.PublishGenericAPIView9childrenByPK.as_view()), views.py # 基于GenericAPIView的9个子类 需要用哪个就导哪个 有些类是含两个功能 from rest_framework.generics import ListAPIView,UpdateAPIView,\ CreateAPIView,DestroyAPIView,ListCreateAPIView,RetrieveAPIView,\ RetrieveDestroyAPIView,RetrieveUpdateAPIView,RetrieveUpdateDestroyAPIView # 查所有 新增 两个使用方式等价 # class PublishGenericAPIView9children(ListAPIView, CreateAPIView): class PublishGenericAPIView9children(ListCreateAPIView): queryset = models.Publish.objects serializer_class = PublishSerializers class PublishGenericAPIView9childrenByPK(UpdateAPIView,DestroyAPIView,RetrieveAPIView): queryset = models.Publish.objects serializer_class = PublishSerializers
--- 基于ModelViewSet类写views.py中的五个接口函数
urls.py path('publishes/', views.PublishModelViewSet.as_view({'get':'list','post':'create'})), re_path('publishes/(?P<pk>\d+)/', views.PublishModelViewSet.as_view(actions={'get':'retrieve','put':'update','delete':'destroy'}) # 用关键字命名最好 views.py # 基于ModelViewSet写5个接口 from rest_framework.viewsets import ModelViewSet class PublishModelViewSet(ModelViewSet): queryset = models.Publish.objects serializer_class = PublishSerializers
--- 自动生成路由
# 第一步 导入routers中的两个类 from rest_framework.routers import SimpleRouter,DefaultRouter # 第二步 实例化得到对象 router = SimpleRouter() # 自动生成两条路由 一般用它 # router = DefaultRouter() # 自动生成六条路由 不推荐→ # 第三步 注册 # router.register('前缀', 继承ModelViewSet的视图类, [‘别名’]) router.register('publishes', views.PublishModelViewSet) # 不加斜杠/ """ print(router.urls) [<URLPattern '^publishes/$' [name='publish-list']>, <URLPattern '^publishes/(?P<pk>[^/.]+)/$' [name='publish-detail']>] """ # 第四步 将自动生成路由加入路由列表中 在urlpatterns下书写 urlpatterns += router.urls
--- action装饰器使用
装饰器action用于给在继承ModelViewSet的视图类中自定义的函数也添加路由
'''views.py''' from rest_framework.viewsets import ModelViewSet from rest_framework.decorators import action class PublishModelViewSet(ModelViewSet): queryset = models.Publish.objects serializer_class = PublishSerializers @action(methods=['get','post'], detail=False) """ methods是访问此路由时来的可执行请求 不区分大小写 内部自动.lower() detail布尔类型 为False时自动路由为publishes/get_all/$ [name='publish-get-all'] """ def get_all(self, request): # print(request.method) # 可通过request.method来分别做get请求和post请求的事! publish_obj = self.get_queryset() # publish_obj = self.get_queryset()[:10] # 截取,前十条 publish_ser_obj = self.get_serializer(publish_obj, many=True) return Response(publish_ser_obj.data) @action(methods=['get'], detail=True) """ detail为True时 自动生成路由匹配为publishes/(?P<pk>[^/.]+)/get_1/$ [name='publish-get-1'] 注意pk在中间 """ def get_1(self, request, pk): publish_obj = self.get_object() publish_ser_obj = self.get_serializer(publish_obj) return Response(publish_ser_obj.data)
总结
一般写序列化组件用APIView原生,可扩展功能多
GenericAPIView用于操作数据库时方便