前言
1、自动生成符合restful规范的API
2、自动为 API 生成浏览器页面
3、权限管理
本次演示
1 、restframework快速实现接口增删改查
2、统一自定义返回字段
3、统一异常处理
4、自定义权限
5、自定义分页
6、自定义复杂接口操作
开始
首先,
1、初始化框架,安装djangorestframework==3.6.3
2、创建app。python manage.py startapp rest
3、添加app到 INSTALLED_APPS
创建models
class TEST(models.Model):
name = models.CharField(u'名称',max_length=50)
time = models.DateTimeField(u'时间',default=datetime.datetime.now)
class Meta:
ordering = ('-time',)
序列化
serializer.py
class TestSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = TEST
fields = ['id','name','time']
#fields = '__all__'
set
views.py
from component.drf import viewsets as component_viewsets
class TestViewSet(component_viewsets.ModelViewSet):
queryset = TEST.objects.all()
serializer_class = TestSerializer
viewsets.py
from rest_framework import viewsets
from rest_framework.views import APIView
from mixins import ApiGenericMixin
class APIView(ApiGenericMixin, APIView):
"""APIView"""
pass
class ModelViewSet(ApiGenericMixin, viewsets.ModelViewSet):
"""按需改造DRF默认的ModelViewSet类"""
pass
class ReadOnlyModelViewSet(ApiGenericMixin, viewsets.ReadOnlyModelViewSet):
"""按需改造DRF默认的ModelViewSet类"""
pass
class ViewSet(ApiGenericMixin, viewsets.ViewSet):
"""按需改造DRF默认的ViewSet类"""
pass
class GenericViewSet(ApiGenericMixin, viewsets.GenericViewSet):
"""按需改造DRF默认的GenericViewSet类"""
pass
统一结果返回
from rest_framework import status
from rest_framework.response import Response
from component.constants import ResponseCodeStatus
class ApiGenericMixin(object):
"""API视图类通用函数"""
# TODO 权限部分加载基类中
permission_classes = ()
def finalize_response(self, request, response, *args, **kwargs):
"""统一数据返回格式"""
if response.data is None:
response.data = {
'result': True,
'code': ResponseCodeStatus.OK,
'message': 'success',
'data': []
}
elif isinstance(response.data, (list, tuple)):
response.data = {
'result': True,
"code": ResponseCodeStatus.OK,
"message": 'success',
"data": response.data,
}
elif isinstance(response.data, dict) and "code" not in response.data:
response.data = {
'result': True,
"code": ResponseCodeStatus.OK,
"message": 'success',
"data": response.data,
}
if response.status_code == status.HTTP_204_NO_CONTENT and request.method == "DELETE":
response.status_code = status.HTTP_200_OK
# response.status_text = "OK"
return super(ApiGenericMixin, self).finalize_response(
request, response, *args, **kwargs
)
注册路由
urls.py
router = routers.DefaultRouter(trailing_slash=True)
router.register(r'test_a', views.TestViewSet,base_name='test')
urlpatterns = router.urls
ok,runserver,打开浏览器访问

打开postman,测试增删改查,成功!
扩展
统一异常处理是在settings里面配置
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'component.generics.exception_handler',
'DEFAULT_PAGINATION_CLASS': 'component.drf.pagination.CustomPageNumberPagination',#自定义分页代码在下面扩展里面
'PAGE_SIZE': 1,#每页数量
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
),
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
'DATETIME_FORMAT': "%Y-%m-%d %H:%M:%S"
}
统一异常处理代码
from django.conf import settings
from rest_framework import status
from rest_framework.compat import set_rollback
from rest_framework.response import Response
from rest_framework.exceptions import (AuthenticationFailed,MethodNotAllowed,NotAuthenticated, PermissionDenied,ValidationError)
from django.http import Http404
from common.log import logger
from constants import ResponseCodeStatus
import traceback
def exception_handler(exc, context):
"""
异常统一处理处理,拷贝自paas-ci项目
分类:
rest_framework框架内异常
app自定义异常
"""
data = {
'result': False,
'data': None
}
if isinstance(exc, (NotAuthenticated, AuthenticationFailed)):
data = {
'result': False,
'code': ResponseCodeStatus.UNAUTHORIZED,
'detail': u"用户未登录或登录态失效,请使用登录链接重新登录",
'login_url': '',
}
return Response(data, status=status.HTTP_403_FORBIDDEN)
if isinstance(exc, PermissionDenied):
data = {
'result': False,
'code': ResponseCodeStatus.PERMISSION_DENIED,
'message': exc.detail,
}
return Response(data, status=status.HTTP_403_FORBIDDEN)
else:
if isinstance(exc, ValidationError):
data.update({
'code': ResponseCodeStatus.VALIDATE_ERROR,
'messages': exc.detail,
})
elif isinstance(exc, MethodNotAllowed):
data.update({
'code': ResponseCodeStatus.METHOD_NOT_ALLOWED,
'message': exc.detail,
})
elif isinstance(exc, PermissionDenied):
data.update({
'code': ResponseCodeStatus.PERMISSION_DENIED,
'message': exc.detail,
})
# elif isinstance(exc, ServerError):
# # 更改返回的状态为为自定义错误类型的状态码
# data.update({
# 'code': exc.code,
# 'message': exc.message,
# })
elif isinstance(exc, Http404):
# 更改返回的状态为为自定义错误类型的状态码
data.update({
'code': ResponseCodeStatus.OBJECT_NOT_EXIST,
'message': u"当前操作的对象不存在",
})
else:
# 调试模式
logger.error(traceback.format_exc())
print traceback.format_exc()
if settings.RUN_MODE != 'PRODUCT':
raise exc
# 正式环境,屏蔽500
data.update({
'code': ResponseCodeStatus.SERVER_500_ERROR,
'message': exc.message,
})
set_rollback()
return Response(data, status=status.HTTP_200_OK)
分页
# -*- coding: utf-8 -*-
from collections import OrderedDict
from rest_framework.pagination import LimitOffsetPagination, PageNumberPagination
from rest_framework.response import Response
class CustomPageNumberPagination(PageNumberPagination):
"""
自定义分页格式,综合页码和url
"""
page_size_query_param = 'page_size'
max_page_size = 10000
def get_paginated_response(self, data):
return Response(OrderedDict([
('page', self.page.number),
('total_page', self.page.paginator.num_pages),
('count', self.page.paginator.count),
('next', self.get_next_link()),
('previous', self.get_previous_link()),
('items', data)
]))
用到的转换函数
# -*- coding: utf-8 -*-
from collections import Counter, namedtuple
def tuple_choices(tupl):
"""从django-model的choices转换到namedtuple"""
return [(t, t) for t in tupl]
def dict_to_namedtuple(dic):
"""从dict转换到namedtuple"""
return namedtuple('AttrStore', dic.keys())(**dic)
def choices_to_namedtuple(choices):
"""从django-model的choices转换到namedtuple"""
return dict_to_namedtuple(dict(choices))
def tuple_to_namedtuple(tupl):
"""从tuple转换到namedtuple"""
return dict_to_namedtuple(dict(tuple_choices(tupl)))
# -*- coding: utf-8 -*-
from django.utils.translation import ugettext as _
from .utils.basic import (choices_to_namedtuple, tuple_choices)
# 返回状态码
CODE_STATUS_TUPLE = (
"OK",
'UNAUTHORIZED',
'VALIDATE_ERROR',
'METHOD_NOT_ALLOWED',
'PERMISSION_DENIED',
"SERVER_500_ERROR",
"OBJECT_NOT_EXIST"
)
CODE_STATUS_CHOICES = tuple_choices(CODE_STATUS_TUPLE)
ResponseCodeStatus = choices_to_namedtuple(CODE_STATUS_CHOICES)
复杂接口查询
views.py
from django.shortcuts import render
from rest.models import *
from rest_framework import viewsets
from serializer import TestSerializer
from component.drf import viewsets as component_viewsets
from rest.permissions import IsAdmin
from rest_framework.decorators import list_route,detail_route
from rest_framework.response import Response
"""
ModelViewSet 默认get获取所有数据,post添加数据,put更新,delete删除
ReadOnlyModelViewSet
"""
from django.views.decorators.csrf import csrf_exempt
class TestViewSet(component_viewsets.ModelViewSet):
queryset = TEST.objects.all()
serializer_class = TestSerializer
#自定义权限
permission_classes = (IsAdmin,)
# 搜索和获取所有的
@list_route(methods=["post"],url_path='list')
def get_list(self, request, *args, **kwargs):
keys = request.data.get('keys',None)
records = self.queryset.filter(name__contains=keys)
result=[]
for item in records:
result.append({
'id':item.id,
'title':item.name,
'create_time':item.time
})
return Response(result)
@list_route(methods=["post"],url_path='add')
def add_obj(self, request, *args, **kwargs):
return Response('POST请求,响应内容')
自定义权限
permissions.py
# -*- coding: utf-8 -*-
from rest_framework import permissions
class IsAdmin(permissions.BasePermission):
message = u"对不起,您没有该操作的权限,请联系管理员。"
def has_permission(self, request, view):
if request.user.is_superuser:
return True
return False
本文详细介绍使用Django REST framework实现RESTful API的过程,包括自动化的API生成、浏览器页面展示、权限管理、接口操作及自定义功能,如返回字段、异常处理、权限、分页和复杂接口操作。
2713

被折叠的 条评论
为什么被折叠?



