目录
--- 基于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接口的代码
--安装
pip install djangorestframework
--基本使用
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/通过请求方式的不同调用不同的视图函数
--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
'''上传的文件'''
-序列化组件
--基本介绍
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()保存修改后的数据时,会报错:
NotImplementedError: `update()` must be implemented.
说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
---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用于操作数据库时方便