DRF(Django Rest Framework)记录
DRF(Django REST Framework)使用文档
作用:简化代码编写,提高开发速度。
其中继承关系如下:
。序列化
serializer -> ModelSerializer
。视图
APIView -> GenericAPIView
-> Mixin扩展类(ListModelMixin,RetrieveModelMixin,CreateModelMixin,UpdateModelMixin,DestroyModelMixin)
-> 一些继承于以上的扩展类
。视图集
ViewSet
GenericViewSet -> ReadOnlyModelViewSet
-> ModelViewSet
安装和配置
环境依赖
python(2.7,3.4,3.5,3.6,3.7),django(1.11,2.0,2.1)
安装
pip install djangorestframework
配置(注册)
vi settings.py
INSTALLED_APPS = [
'rest_framework' #DRF注册
]
一个基本的DRF快速创建流程
- 创建序列化器类(serializer)
- 创建视图,继承视图集(ModelViewSet)
- 创建动态路由(DefaultRouter)
- web进行展示查看
序列化和反序列化
. 序列化就是将数据库查询的模型对象转换为字典。
. 反序列化就是将前端提交的字典转换为模型。
序列化器---serializer
from rest_framework.serializers import Serializer
示例
class BookInfoSerializer(serializers.Serializer): id = serializer.Intergent(label='ID', read_only=True) btitle = serializers.CharField(max_length=20, label='书名',required=True) bpub_date = serializers.DateField(label='发布时间', required=True) bread = serializers.CharField(max_length=20, label='点击量', required=False) bcomment = serializers.CharField(max_length=20,label='评论量', required=False) # heroinfo_set = serializers.PrimaryKeyRelatedField(many=True)
def validate_btitle(self, value):
'''额外追加的校验逻辑,单独对字段进行校验'''
if 'python' not in value:
raise serializers.ValidationError('图书不是关于django的')
return value
def validate(self, attrs):
'''对多个字段联合校验
attrs:为前端传入的所有数据
'''
#这里可以对2个密码进行匹配验证,也可以修改前端传入的数据
# attrs['hello'] = 'world'
return attrs
def create(self, validated_data):
'''调用序列化器的save方法时,没有给instance传参,则创建数据'''
book = BookInfo.objects.create(**validated_data)
return book
def update(self, instance, validated_data):
'''调用序列化器的save方法,给instance传参,则更新数据'''
instance.btitle = validated_data.get('btitle')
instance.bpub_date = validated_data.get('bput_date')
instance.save()
return instance
#以下是外键的三种写法
# hbook = serializers.PrimaryKeyRelatedField(label='书籍', read_only=True) # 只做序列化
# hbook = serializers.PrimaryKeyRelatedField(label='书籍', queryset=BookInfo.objects.all()) # 用于序列化和反序列化,会检查赋值是否在queryset中存在
# hbook = BookInfoSerializer() # 将关联模型对象的序列化器中所有字段序列化出来(就是模型对象的数据)
说明:serializers.PrimaryKeyRelatedField 将关联模型的ID序列化,
serializers.StringRelatedField 将关联模型的字符串序列化
BookInfoSerializer()将关联模型所有字段序列化出来
字段的选项参数:
max_length, min_length: 最大/最小长度
allow_blank:是否允许为空
trim_whitespace:是否截断空白字符
max_value,min_value:最大,最小值
通用参数:
read_only:只用作序列化,只读
write_only:只用作反序列化,写入
required:是否必须输入,True / False
default:反序列化使用的默认值
allow_null:是否允许传入null,默认False
validators:该字段使用的验证器。
error_messages:包含错误编号和错误信息的字典
label:api页面展示的字段名称
help_text:api页面,展示的字段帮助提示。
序列化器---Modelserializer
继承自:Serializer,和Serializer区别
1. modelserializer可以根据模型自动生成序列化器中的字段
2. modelserializer里面已经帮我们实现了create和update方法
示例
from rest_framework.serializers import ModelSerializer
class BookInfoSerializer(ModelSerializer):
class Meta:
model = BookInfo #指定序列化模型名称
fields = '__all__' #表示映射模型的所有字段
fields = ['id','btitle','bread'] #指定显示字段
exclude = ['image'] #除了image字段,其他都显示
extra_kwargs = {
'bread': {'min_value':0,'required':True} #当模型默认字段不符合要求,这里可以自定义选项值
'bcomment':{'min_value':0, 'write_only':True}
}
read_only_fields = ['id','bread'] #指定字段只做序列化
视图
Request和Response
request
.data:返回解析之后的请求体数据。类似django的request.POST和request.FILES。
.query_params:类似django的request.GET
response
rest framework提供的一个响应类,响应的数据内容会被转换为符合前端需求的类型。
格式:Response(data, status=None,template_name=None,headers=None,content_type=None)
参数说明:
data:为响应准备的序列化处理后的数据
status:状态码,默认200
template_name:模板名称,
headers:存放响应头信息的字典
content_type:响应数据的Content_type,通常此参数不需要传递。
状态码:from rest_framework import status
APIView
继承自:django的view,和view的区别
1. 传入到视图中的是request对象
2. 视图返回的是response对象
3. APIException异常都会捕获到
4. 进行dispatch()分发前,会对请求进行身份认证,权限检查,流量控制。
支持定义的属性:
。authentication_classes 列表或元祖,身份认证类
。permission_classes 列表或元祖,权限检查类
。throttle_classes 列表或元祖,流量控制类
示例
from rest_framework.views import APIView
from app1 import models
from app1.serializer import BookInfoSerializer
from rest_framework.response import Response
from rest_framework import status
class BookInfoAPIView(APIView):
def get(self, request):
'''查询所有'''
qs = models.BookInfo.objects.all()
serializer = BookInfoSerializer(instance=qs, many=True)
return Response(serializer.data)
def post(self, request):
'''新增'''
# 获取前端传入的数据
data = request.data
# 创建序列化器进行反序列化
serializer = BookInfoSerializer(data=data)
# 调用序列化器的is_valid进行校验
serializer.is_valid(raise_exception=True)
# 调用序列化器的save方法执行create操作
serializer.save()
# 响应前端
return Response(serializer.data, status=status.HTTP_201_CREATED)
class BookDetailAPIView(APIView):
def get(self, request, pk):
'''查询单一'''
try:
qs = models.BookInfo.objects.get(id=pk)
except models.BookInfo.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = BookInfoSerializer(qs)
return Response(serializer.data, status=status.HTTP_202_ACCEPTED)
def put(self,request,pk):
'''修改单一'''
try:
book = models.BookInfo.objects.get(id=pk)
except models.BookInfo.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = BookInfoSerializer(instance=book, data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
def delete(self,request, pk):
'''删除单一'''
try:
book = models.BookInfo.objects.get(id=pk)
except models.BookInfo.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
book.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
GenericAPIView
继承自APIView,和APIView区别 1. 增加了序列化器 2. 增加了数据库查询方法 3. 为Mixin扩展类提供方法支持。 4. 新增分页控制器 5. 新增过滤控制
示例
from rest_framework.generics import GenericAPIView
class BookInfoGenericAPIView(GenericAPIView):
# 定义查询集,"数据来源"
queryset = models.BookInfo.objects.all()
# 定义序列化器类
serializer_class = BookInfoSerializer
def get(self,request):
#返回视图使用的查询集
qs = self.get_queryset()
# 返回序列化器对象,进行序列化
serializer = self.get_serializer(qs, many=True)
return Response(serializer.data)
def post(self,request):
# 进行反序列化
serializer = self.get_serializer(data=request.data)
#进行校验
serializer.is_valid(raise_exception=True)
#保存
serializer.save()
# 返回前端字典
return Response(serializer.data)
class BookDetailGenericAPIView(GenericAPIView):
'''详情视图'''
# 定义查询集,"数据来源"
queryset = models.BookInfo.objects.all()
# 定义序列化器类
serializer_class = BookInfoSerializer
def get(self,request, pk):
#返回详情视图所需的模型类数据对象
qs = self.get_object()
serializer = self.get_serializer(qs)
return Response(serializer.data)
def put(self, request, pk):
book = self.get_object()
# 进行反序列化,调用父类中的update方法更新数据,instance为原数据,data为新数据
serializer = self.get_serializer(instance=book, data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data)
def delete(self, request, pk):
book = self.get_object()
book.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
扩展类Mixin
有5个:ListModelMixin,CreateModelMixin,RetrieveModelMixin,DestroyModelMixin,UpdateModelMixin,都是配合GenericAPIView使用,是兄弟关系。
示例
from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin,DestroyModelMixin,UpdateModelMixin
class BookInfoGenericAPIView(ListModelMixin, CreateModelMixin, GenericAPIView):
# 定义查询集,"数据来源",主要为Mixin使用
queryset = models.BookInfo.objects.all()
# 定义序列化器类,主要为Mixin使用
serializer_class = BookInfoSerializer
def get(self,request):
# 返回父类方法
return self.list(request)
def post(self,request):
# 返回父类方法
return self.create(request)
class BookDetailGenericAPIView(RetrieveModelMixin, UpdateModelMixin,DestroyModelMixin, GenericAPIView):
'''详情视图'''
# 定义查询集,"数据来源",主要为Mixin使用
queryset = models.BookInfo.objects.all()
# 定义序列化器类,主要为Mixin使用
serializer_class = BookInfoSerializer
def get(self,request, pk):
return self.retrieve(request)
def put(self, request, pk):
return self.update(request)
def delete(self, request, pk):
return self.destroy(request)
扩展类
继承关系
ListAPIView:继承自(ListModelMixin,GenericAPIView),查所有
CreateAPIView:继承自(CreateModelMixin,GenericAPIView),新增
RetrieveAPIView:继承自(RetrieveModelMixin,GenericAPIView),查单一
UpdateAPIView:继承自(UpdateModelMixin,GenericAPIView),更新单一
DestroyAPIView:继承自(DestroyModelMixin,GenericAPIView),删除单一
ListCreateAPIView:继承自(ListModelMixin,CreateModelMixin,GenericAPIView),查所有和新增
RetrieveUpdateAPIView:继承自(RetrieveModelMixin,UpdateModelMixin,GenericAPIView)查单一和更新单一
RetrieveDestroyAPIView:继承自(RetrieveModelMixin,DestroyModelMixin,GenericAPIView)查单一和删除单一
RetrieveUpdateDestroyAPIView:继承自(RetrieveModelMixin,UpdateModelMixin, DestroyModelMixin,GenericAPIView),查单一更新单一删除单一
示例
from rest_framework.generics import GenericAPIView, ListAPIView, CreateAPIView, ListCreateAPIView,RetrieveAPIView,UpdateAPIView,DestroyAPIView, RetrieveUpdateDestroyAPIView
class BookInfoGenericAPIView(CreateModelMixin, ListAPIView): 或者 class BookInfoGenericAPIView(ListCreateAPIView):
# 定义查询集,"数据来源"
queryset = models.BookInfo.objects.all()
# 定义序列化器类
serializer_class = BookInfoSerializer
class BookDetailGenericAPIView(RetrieveAPIView,UpdateAPIView,DestroyAPIView): 或者 class BookDetailGenericAPIView(RetrieveUpdateDestroyAPIView):
'''详情视图'''
# 定义查询集,"数据来源"
queryset = models.BookInfo.objects.all()
# 定义序列化器类
serializer_class = BookInfoSerializer
视图集
可以将所有的增删改查写到一个视图函数中,默认已定义:list():查所有,retrieve():查单一,create():创建,update():更新,destroy():删除
ViewSet
继承自:APIView和ViewSetMixin
ViewSet示例
1. 视图函数
from rest_framework.viewsets import ViewSet
class BookViewSet(ViewSet):
def list(self,request):
'''定义查所有方法'''
qs = models.BookInfo.objects.all()
serializer = BookInfoSerializer(qs, many=True)
return Response(serializer.data)
def retrieve(self, request, pk):
'''定义查单一方法 '''
try:
qs = models.BookInfo.objects.get(id=pk)
except models.BookInfo.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = BookInfoSerializer(qs)
return Response(serializer.data)
2. 路由
url(r'^viewset-books/$', views.BookViewSet.as_view({'get':'list'})), #创建路由映射,来代替get,post方法
url(r'^viewset-books/(?P<pk>\d+)/$', views.BookViewSet.as_view({'get':'retrieve'})),
GenericViewSet
继承自:GenericAPIView与ViewSetMixin,
GenericViewSet示例
1. 视图
from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin,DestroyModelMixin,UpdateModelMixin
class BookGenericcViewSet(ListModelMixin,RetrieveModelMixin,CreateModelMixin,UpdateModelMixin,DestroyModelMixin, GenericViewSet):
queryset = models.BookInfo.objects.all()
serializer_class = BookInfoSerializer
2. 路由
url(r'^genericviewset-books/$', views.BookGenericcViewSet.as_view({'get':'list','post':'create'})),
url(r'^genericviewset-books/(?P<pk>\d+)/$',views.BookGenericcViewSet.as_view({'get':'retrieve','put':'update','delete':'destroy'}))
ReadOnlyModelViewSet
继承自:ListModelMixin,RetrieveModelMixin,GenericViewSet
ReadOnlyModelViewSet示例
from rest_framework.viewsets import ReadOnlyModelViewSet
from rest_framework.mixins import CreateModelMixin,DestroyModelMixin,UpdateModelMixin
class BookGenericcViewSet(CreateModelMixin,UpdateModelMixin,DestroyModelMixin,ReadOnlyModelViewSet ):
queryset = models.BookInfo.objects.all()
serializer_class = BookInfoSerializer
路由同上
ModelViewSet
继承自:ListModelMixin,RetrieveModelMixin,CreateModelMixin,UpdateModelMixin,DestroyModelMixin,GenericViewSet
ModelViewSet示例
from rest_framework.viewsets import ModelViewSet
class BookModelViewSet(ModelViewSet):
queryset = models.BookInfo.objects.all()
serializer_class = BookInfoSerializer
路由同上
视图集自定义方法实现独立功能
视图集默认已定义:list,create,update,destroy,retrieve 5中方法,如实现其他独立功能,需要自定义。
自定义独立功能示例
提示:action方法用于动态路由,不加action方法,可以用静态路由。
1. 视图
from rest_framework.viewsets import ModelViewSet
from rest_framework.decorators import action
class BookModelViewSet(ModelViewSet):
queryset = models.BookInfo.objects.all()
serializer_class = BookInfoSerializer
#定制方法实现独立功能
@action(methods=['get'], detail=False) # 不存在pk,detail为False
def latest(self, request):
qs = models.BookInfo.objects.latest('id') #获取最后一本书
serializer = self.get_serializer(qs) #进行序列化
return Response(serializer.data)
@action(methods=['get'], detail=True)
def read(self, request, pk):
'''修改图书阅读量'''
book = self.get_object()
book.read = request.data.get('bread')
book.save()
serializer = self.get_serializer(book)
return Response(serializer.data)
2. 路由
有2种写法。
第一种静态写法:
如果此行为不需要PK,那么就是列表视图,但列表视图默认是list,create,
url(r'^books/latest/$', views.BookModelViewSet.as_view({'get':'latest'})),
如果需要pk,那么增加如下
url(r'^books/(?P<pk>\d+)/read/$', views.BookModelViewSet.as_view({'put':'read'})),
第二种动态写法(只有视图集才支持动态,方法前需要action方法装饰):
from rest_framework.routers import DefaultRouter
DefaultRouter继承自SimpleRouter(),唯一区别是当请求不存在路径时SimpleRouter会报错,DefaultRouter会多生成根路由,不会报错。
router = DefaultRouter() #创建路由器,默认只有增删改查,额外的方法需要增加action装饰器即可。
router.register(r'model-books', views.BookModelViewSet) #注册路由
urlpatterns += router.urls #把生成好的路由拼接到urlpatterns
router.register(prefix, viewset, base_name)参数解释
.prefix:视图集的路由前缀
.viewset:视图集
.base_name:路由名称的前缀,如不指定,默认会找视图集中queryset属性所指定查询集对应模型名小写作为路由别名前缀,
但如果没有设置queryset,就必须为base_name赋值。
认证,权限,限流,过滤,排序,分页
视图单独认证
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framwork.views import APIView
class ExampleView(APIView):
authentication_classes = (SessionAuthentication, BasicAuthentication)
视图单独权限配置
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
class ExampleView(APIView):
permission_classes = (IsAuthenticated,)
权限的四个级别
。 AllowAny 允许所有用户
。 IsAuthenticated 仅通过认证的用户
。 IsAdminUser 仅管理员用户
。 IsAuthenticatedOrReadOnly:认证的用户可以完全操作,否则只能get读取
自定义权限示例
继承rest_framework.permissions.BasePermission父类,重写方法
。has_permission(self, request, view):对整个视图增删改查
。has_object_permission(self, request, view, obj):对单一视图的数据对象增删改查。
返回 True 或 False
class MyPermission(BasePermission):
def has_object_permission(self, request, view, obj):
'''控制对obj对象的访问权限'''
return False
class BookInfoView(ModelViewSet):
permission_classes = [IsAuthenticated, MyPermission]
限流
对接口访问的频次进行限制
限流示例
from rest_framework.throttling import UserRateThrottle
from rest_framework.views import APIView
class ExampleView(APIView):
throttle_classes = (UserRateThrottle)
可选的限流类:
1):AnonRateThrottle:限制所有匿名未认证用户,使用IP区分用户,使用DEFAULT_THROTTLE_RATES['anon']来限制频次
2):UserRateThrottle:限制认证用户,使用user id来区分,使用DEFAULT_THROTTLE_RATES['user']来设置频次
3):ScopedRateThrottle:限制用户对于每个视图的访问频次,使用ip或user id,
过滤
1. 对列表数据根据字段进行过滤,可通过添加django-filter扩展来增强支持
pip install django-filter
2. 需要进行注册:
INSTALLED_APPS = [
'django_filters', #注册应用
]
3. 配置文件进行配置
4. 视图中添加filter_fields属性,指定过滤的字段
class BookListView(ListAPIView):
filter_fields = ('btitle','bread','id')
5. URL访问:ip:8000/books/?btitle=西游记
常见参数:
?limit=10:返回记录的数量
?offset=10:返回记录的开始位置
?page=26&per_page=100:返回第几页,每页的记录数
?sortby=name&order=asc:返回按哪个属性排序,以及排序顺序
?animal_type_id=1:指定筛选条件
排序
根据前端传递的ordering参数指明的排序字段进行排序。
排序示例
1. 全局配置文件设置
2. 视图指明过滤字段和排序字段
from rest_framework.filters import OrderingFilter
class BookModelViewSet(ModelViewSet):
#指定过滤字段
filter_fields = ['id','btitle','bread']
#指定过滤后端为排序
filter_backends = [OrderingFilter]
# 指定排序字段
ordering_fields = ['id','bread']
# 127.0.0.1:8000/books/?ordering=-id 按照ID进行降序排序
# 127.0.0.1:8000/books/?ordering=bread #标识按照bread进行排序
分页
1. 全局分页参考settings.py配置
2. 自定义分页PageNumberPagination示例:
from rest_framework.pagination import PageNumberPagination
class LargeResultSetPag(PageNumberPagination):
'''自定义分页'''
page_size = 2 #默认每页显示条数
page_query_param = 'page' # 前端查询字符串用的关键字,显示第几页用的名字
page_size_query_param = 'page_size' #前端查询字符串用的关键字,控制每页显示多少条的关键字
max_page_size = 5 #前端控制每页显示多少条,最多不能超过5条
class BookModelViewSet(ModelViewSet):
pagination_class = LargeResultSetPag
#127.0.0.1:8000/books/?page=1&page_size=2 #表第一页,每页2条
3. 自定义分页LimitOffsetPagination示例:
from rest_framework.pagination import LimitOffsetPagination
class LimitOffset(LimitOffsetPagination):
default_limit = 2 #默认查看条数
max_limit = 5 #限制前端每次最多查看条数
limit_query_param = 'limit' # 表示查看多少条(关键字)
offset_query_param = 'offset' #表示偏移,从第几条数据查看(关键字)
class BookListView(ListAPIView):
pagination_class = LimitOffset
# http://127..:8000/books/offset=3&limit=2 表示从第3条数据看,一共看2个
异常
REST Framework定义的异常
。APIException 所有异常的父类
。ParseError 解析错误
。AuthenticationFailed 认证失败
。NoAuthenticated 尚未认证
。PermissionDenied 权限拒绝
。NotFound 未找到
。MethodNotAllowed 请求方式不支持
。NotAcceptable 要获取的数据格式不支持
。Throttled 超过限流次数
。ValidationError 校验失败
自定义异常示例
1. 在app下创建异常文件: exceptions.py
from rest_framework.views import exception_handler as drf_exception_handler
from django.db import DatabaseError
from rest_framework.response import Response
from rest_framework import status
def exception_handler(exc, context):
'''在DRF捕获基础上额外增加数据库的异常捕获'''
# 先调用REST framework默认的异常处理方式获得标准错误响应方式
response = drf_exception_handler(exc, context)
#这里补充自定义的异常处理
if response is None: # 如果没有拦截住
view = context['view']
if isinstance(exc, DatabaseError): # 判断是否为数据库的异常
print('[%s]: %s' % (view, exc))
response = Response({'detail': '服务器内部错误'},status = status.HTTP_507_INSUFFICIENT_STORAGE)
return response
2. setting.py中声明,如果没有声明,默认值为:'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler'
3. 测试,在视图中的方法添加:
from django.db import DatabaseError
raise DatabaseError()
自动生成接口文档(只对DRF的类视图有效)
REST framework可以自动生成接口文档,以网页方式呈现,继承自APIView及其子类的视图。
生成接口文档示例
1. 安装:pip install coreapi
2. 设置接口文档访问路径
from rest_framework.documentation import include_docs_urls
urlpatterns = {
url(r'^docs/', include_docs_urls(title='My API title'))
}
3. 接口文档中的说明字段视图定义:
class BookModelViewSet(ModelViewSet):
'''
list:
返回图书列表数据
retrieve:
返回图书详情数据
latest:
返回最新的图书数据
read:
修改图书的阅读量
'''
4. 访问:# http://127.0.0.1:8000/docs/
配置文件相关配置参数汇总(settings.py)
REST_FRAMEWORK = {
# 修改response的响应格式
'DEFAULT_RENDERER_CLASSES':(
'rest_framework.renderer.JSONRenderer', #JSON渲染器
'rest_framework.renderer.BrowsableAPIRenderer', #游览API渲染器
)
# 开启全局认证
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication', # 基本认证
'rest_framework.authentication.SessionAuthentication' # Session认证
)
# 全局权限配置
'DEFAULT_PERMISSION_CLASSES':(
'rest_framework.permissions.AllowAny',
)
# 限流
'DEFAULT_THROTTLE_CLASSES':( #限流配置
'rest_framework.throttling.AnonRateThrottle', #匿名用户
'rest_framework.throttling.UserRateThrottle' #登录用户
),
'DEFAULT_THROTTLE_RATES':{ #限流频次
'anon':'100/day', # 匿名用户频次,可选参数:second,minute,hour或day
'user':'1000/day' #登录用户频次
}
#指定过滤后端
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
#指定过滤 和 排序
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend','rest_framework.filters.OrderingFilter'),
# 全局分页
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 100 #每页数目
# 自定义异常声明
''EXCEPTION_HANDLER': 'app2.exceptions.exception_handler'
}